banner演示
SpringBoot項目啟動時會在控制臺打印一個默認(rèn)的LOGO满着,這個LOGO就是我們要講的banner。
制作自己的banner
實際上,Spring Boot 支持自定義 logo 的功能。讓我們來看看如何實現(xiàn)的。
只要你在 resources 目錄下放置名為 banner.txt谈喳、banner.gif 、banner.jpg 或 banner.png 的文件戈泼,Spring Boot 會自動加載婿禽,將其作為啟動時打印的 logo。
- 對于文本文件大猛,Spring Boot 會將其直接輸出谈宛。
- 對于圖像文件( banner.gif 、banner.jpg 或 banner.png )胎署,Spring Boot 會將圖像轉(zhuǎn)為 ASCII 字符吆录,然后輸出。
變量
banner.txt 文件中還可以使用變量來設(shè)置字體琼牧、顏色恢筝、版本號。
變量 | 描述 |
---|---|
${application.version} |
MANIFEST.MF 中定義的版本巨坊。如:1.0
|
${application.formatted-version} |
MANIFEST.MF 中定義的版本撬槽,并添加一個 v 前綴。如:v1.0
|
${spring-boot.version} |
Spring Boot 版本趾撵。如:2.1.1.RELEASE . |
${spring-boot.formatted-version} |
Spring Boot 版本侄柔,并添加一個 v 前綴。如:v2.1.1.RELEASE
|
${Ansi.NAME} (or ${AnsiColor.NAME} , ${AnsiBackground.NAME} , ${AnsiStyle.NAME} ) |
ANSI 顏色占调、字體暂题。更多細(xì)節(jié),參考:AnsiPropertySource 究珊。 |
${application.title} |
MANIFEST.MF 中定義的應(yīng)用名薪者。 |
示例:
在 Spring Boot 項目中的 resources 目錄下添加一個名為 banner.txt 的文件,內(nèi)容如下:
${AnsiColor.BRIGHT_YELLOW}${AnsiStyle.BOLD}
________ ___ ___ ________ ___ __ ___ ___
|\ ___ \|\ \|\ \|\ ___ \|\ \ |\ \|\ \|\ \
\ \ \_|\ \ \ \\\ \ \ \\ \ \ \ \ \ \ \ \ \\\ \
\ \ \ \\ \ \ \\\ \ \ \\ \ \ \ \ __\ \ \ \ \\\ \
\ \ \_\\ \ \ \\\ \ \ \\ \ \ \ \|\__\_\ \ \ \\\ \
\ \_______\ \_______\ \__\\ \__\ \____________\ \_______\
\|_______|\|_______|\|__| \|__|\|____________|\|_______|
${AnsiBackground.WHITE}${AnsiColor.RED}${AnsiStyle.UNDERLINE}
:: Spring Boot :: (v${spring-boot.version})
:: Spring Boot Tutorial :: (v1.0.0)
注:${} 設(shè)置字體顏色的變量之間不能換行或空格分隔剿涮,否則會導(dǎo)致除最后一個變量外言津,都不生效。
啟動應(yīng)用后取试,控制臺將打印如下 logo:
推薦兩個生成字符畫的網(wǎng)站悬槽,可以將生成的字符串放入這個banner.txt
文件:
配置
application.properties 中與 Banner 相關(guān)的配置:
# banner 模式。有三種模式:console/log/off
# console 打印到控制臺(通過 System.out)
# log - 打印到日志中
# off - 關(guān)閉打印
spring.main.banner-mode = off
# banner 文件編碼
spring.banner.charset = UTF-8
# banner 文本文件路徑
spring.banner.location = classpath:banner.txt
# banner 圖像文件路徑(可以選擇 png,jpg,gif 文件)
spring.banner.image.location = classpath:banner.gif
used).
# 圖像 banner 的寬度(字符數(shù))
spring.banner.image.width = 76
# 圖像 banner 的高度(字符數(shù))
spring.banner.image.height =
# 圖像 banner 的左邊界(字符數(shù))
spring.banner.image.margin = 2
# 是否將圖像轉(zhuǎn)為黑色控制臺主題
spring.banner.image.invert = false
編程方式設(shè)置banner的兜底邏輯
@SpringBootApplication
@MapperScan("com.yibo.source.code.mapper")//掃描Mapper接口
public class Application {
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(Application.class);
springApplication.setBanner(new ResourceBanner(new ClassPathResource("banner_bak.txt")));
springApplication.run(args);
}
}
banner_bak.txt
////////////////////////////////////////////////////////////////////
// _ooOoo_ //
// o8888888o //
// 88" . "88 //
// (| ^_^ |) //
// O\ = /O //
// ____/`---'\____ //
// .' \\| |// `. //
// / \\||| : |||// \ //
// / _||||| -:- |||||- \ //
// | | \\\ - /// | | //
// | \_| ''\---/'' | | //
// \ .-\__ `-` ___/-. / //
// ___`. .' /--.--\ `. . ___ //
// ."" '< `.___\_<|>_/___.' >'"". //
// | | : `- \`.;`\ _ /`;.`/ - ` : | | //
// \ \ `-. \_ __\ /__ _/ .-` / / //
// ========`-.____`-.___\_____/___.-`____.-'======== //
// `=---=' //
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //
// 佛祖保佑 永無故障 永不修改 //
////////////////////////////////////////////////////////////////////
輸出:
注意:
默認(rèn)瞬浓,Spring Boot 會注冊一個 SpringBootBanner 的單例 Bean初婆,用來負(fù)責(zé)打印 Banner。
如果想完全個人定制 Banner,可以這么做:先實現(xiàn) org.springframework.boot.Banner#printBanner 接口來自己定制 Banner烟逊。在將這個 Banner 通過 SpringApplication.setBanner(…) 方法注入 Spring Boot。
banner獲取原理
banner的入口在SpringApplication的run方法中:
public class SpringApplication {
public ConfigurableApplicationContext run(String... args) {
......
Banner printedBanner = printBanner(environment);
}
}
接著跟進(jìn)printBanner(environment);方法
private Banner printBanner(ConfigurableEnvironment environment) {
//判斷是否關(guān)閉打印
if (this.bannerMode == Banner.Mode.OFF) {
return null;
}
ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader
: new DefaultResourceLoader(getClassLoader());
SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);
//判斷是否打印到日志中打印到日志中
if (this.bannerMode == Mode.LOG) {
return bannerPrinter.print(environment, this.mainApplicationClass, logger);
}
//直接打印到控制臺
return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}
接著跟進(jìn)bannerPrinter.print()方法
public Banner print(Environment environment, Class<?> sourceClass, Log logger) {
Banner banner = getBanner(environment);
try {
logger.info(createStringFromBanner(banner, environment, sourceClass));
}
catch (UnsupportedEncodingException ex) {
logger.warn("Failed to create String for banner", ex);
}
return new PrintedBanner(banner, sourceClass);
}
public Banner print(Environment environment, Class<?> sourceClass, PrintStream out) {
Banner banner = getBanner(environment);
banner.printBanner(environment, sourceClass, out);
return new PrintedBanner(banner, sourceClass);
}
不管是輸出到日志還是打印到控制臺都會調(diào)用getBanner(Environment environment)方法
private Banner getBanner(Environment environment) {
Banners banners = new Banners();
banners.addIfNotNull(getImageBanner(environment));
banners.addIfNotNull(getTextBanner(environment));
if (banners.hasAtLeastOneBanner()) {
return banners;
}
if (this.fallbackBanner != null) {
return this.fallbackBanner;
}
return DEFAULT_BANNER;
}
getImageBanner(environment)
- 可以通過
spring.banner.image.location
指定位置 - 可以使用的圖片格式為
"gif", "jpg", "png"
static final String BANNER_IMAGE_LOCATION_PROPERTY = "spring.banner.image.location";
static final String[] IMAGE_EXTENSION = { "gif", "jpg", "png" };
private Banner getImageBanner(Environment environment) {
String location = environment.getProperty(BANNER_IMAGE_LOCATION_PROPERTY);
if (StringUtils.hasLength(location)) {
Resource resource = this.resourceLoader.getResource(location);
return resource.exists() ? new ImageBanner(resource) : null;
}
for (String ext : IMAGE_EXTENSION) {
Resource resource = this.resourceLoader.getResource("banner." + ext);
if (resource.exists()) {
return new ImageBanner(resource);
}
}
return null;
}
getTextBanner(environment)
- 可以通過
spring.banner.location
指定位置 - 默認(rèn)名字為
banner.txt
static final String BANNER_LOCATION_PROPERTY = "spring.banner.location";
static final String DEFAULT_BANNER_LOCATION = "banner.txt";
private Banner getTextBanner(Environment environment) {
String location = environment.getProperty(BANNER_LOCATION_PROPERTY, DEFAULT_BANNER_LOCATION);
Resource resource = this.resourceLoader.getResource(location);
if (resource.exists()) {
return new ResourceBanner(resource);
}
return null;
}
獲取banner步驟
banner輸出原理
默認(rèn)輸出:
- 先輸出banner指定內(nèi)容
- 獲取version信息
- 文本內(nèi)容前后對其
- 文本內(nèi)容染色
- 輸出文本內(nèi)容
class SpringBootBanner implements Banner {
private static final String[] BANNER = { "", " . ____ _ __ _ _",
" /\\\\ / ___'_ __ _ _(_)_ __ __ _ \\ \\ \\ \\", "( ( )\\___ | '_ | '_| | '_ \\/ _` | \\ \\ \\ \\",
" \\\\/ ___)| |_)| | | | | || (_| | ) ) ) )", " ' |____| .__|_| |_|_| |_\\__, | / / / /",
" =========|_|==============|___/=/_/_/_/" };
private static final String SPRING_BOOT = " :: Spring Boot :: ";
private static final int STRAP_LINE_SIZE = 42;
@Override
public void printBanner(Environment environment, Class<?> sourceClass, PrintStream printStream) {
for (String line : BANNER) {
printStream.println(line);
}
String version = SpringBootVersion.getVersion();
version = (version != null) ? " (v" + version + ")" : "";
StringBuilder padding = new StringBuilder();
while (padding.length() < STRAP_LINE_SIZE - (version.length() + SPRING_BOOT.length())) {
padding.append(" ");
}
printStream.println(AnsiOutput.toString(AnsiColor.GREEN, SPRING_BOOT, AnsiColor.DEFAULT, padding.toString(),
AnsiStyle.FAINT, version));
printStream.println();
}
}
文本輸出
- 可以通過
spring.banner.charset
指定字符集 - 獲取文本內(nèi)容
- 替換占位符
- 輸出文本內(nèi)容
private static class Banners implements Banner {
private final List<Banner> banners = new ArrayList<>();
public void addIfNotNull(Banner banner) {
if (banner != null) {
this.banners.add(banner);
}
}
@Override
public void printBanner(Environment environment, Class<?> sourceClass, PrintStream out) {
for (Banner banner : this.banners) {
banner.printBanner(environment, sourceClass, out);
}
}
}
public class ResourceBanner implements Banner {
@Override
public void printBanner(Environment environment, Class<?> sourceClass, PrintStream out) {
try {
String banner = StreamUtils.copyToString(this.resource.getInputStream(),
environment.getProperty("spring.banner.charset", Charset.class, StandardCharsets.UTF_8));
for (PropertyResolver resolver : getPropertyResolvers(environment, sourceClass)) {
banner = resolver.resolvePlaceholders(banner);
}
out.println(banner);
}
catch (Exception ex) {
logger.warn(
"Banner not printable: " + this.resource + " (" + ex.getClass() + ": '" + ex.getMessage() + "')",
ex);
}
}
}
圖片輸出
- 可以通過
spring.banner.image.*
設(shè)置圖片屬性 - 讀取圖片文件流
- 輸出圖片內(nèi)容
private static class Banners implements Banner {
private final List<Banner> banners = new ArrayList<>();
public void addIfNotNull(Banner banner) {
if (banner != null) {
this.banners.add(banner);
}
}
@Override
public void printBanner(Environment environment, Class<?> sourceClass, PrintStream out) {
for (Banner banner : this.banners) {
banner.printBanner(environment, sourceClass, out);
}
}
}
public class ImageBanner implements Banner {
private static final String PROPERTY_PREFIX = "spring.banner.image.";
@Override
public void printBanner(Environment environment, Class<?> sourceClass, PrintStream out) {
String headless = System.getProperty("java.awt.headless");
try {
System.setProperty("java.awt.headless", "true");
printBanner(environment, out);
}
catch (Throwable ex) {
logger.warn("Image banner not printable: " + this.image + " (" + ex.getClass() + ": '" + ex.getMessage()
+ "')");
logger.debug("Image banner printing failure", ex);
}
finally {
if (headless == null) {
System.clearProperty("java.awt.headless");
}
else {
System.setProperty("java.awt.headless", headless);
}
}
}
private void printBanner(Environment environment, PrintStream out) throws IOException {
int width = getProperty(environment, "width", Integer.class, 76);
int height = getProperty(environment, "height", Integer.class, 0);
int margin = getProperty(environment, "margin", Integer.class, 2);
boolean invert = getProperty(environment, "invert", Boolean.class, false);
Frame[] frames = readFrames(width, height);
for (int i = 0; i < frames.length; i++) {
if (i > 0) {
resetCursor(frames[i - 1].getImage(), out);
}
printBanner(frames[i].getImage(), margin, invert, out);
sleep(frames[i].getDelayTime());
}
}
}