今天,我給大家來講講如何讓你的 Spring Boot 屌炸天,自定義 banner 。有些新入門的朋友可能會不知道 banner 是什么?它在哪里?我在哪里見過它嗎?這3連門是不是很有意思。我們今天所說的 banner 如下圖所示,想必大家在啟動 Spring Boot 項目的時候,大家都見過吧。
大家可能都見過永不宕機的佛祖的 banner 圖片吧。下圖,大家應該都很熟悉吧。
今天我就來帶大家來看看源碼,看看這個 banner 到底是怎么實現的。
Spring Boot 內置 3 種 Banner打印方式
從下圖可以看出,Spring Boot 支持打印的Banner 方式有3種, SpringBootBanner 是Spring Boot 默認的 Banner 打印方式, ResouceBanner 是文本類型的 Banner 打印方式, ImageBanner 是圖片類型的 Banner 打印方式。
我們來看看 Banner 接口,從下方的代碼,我們可以了解到,Banner 接口包含一個 printBanner 的方法 和 Mode 的枚舉,Mode 包含三種狀態,其中 OFF 表示不打印 Banner , LOG 表示把 Banner 輸出到日志文件中, CONSOLE 表示輸出到控制臺,也是我們比較常見的方式。
@FunctionalInterface
public interface Banner {
void printBanner(Environment environment, Class< ? > sourceClass, PrintStream out);
enum Mode {
OFF,
CONSOLE,
LOG
}
}
Banner 打印流程
從 SpringApplication 類的 run 方法可以看出,printBanner 方法在 prepareEnvironment 之后,這是因為 application.properties 中有一些關于 Banner 的配置項。需要先解析 application.properties 的值,并將其綁定到對應的 bean 之后,再進行后續的操作。
public ConfigurableApplicationContext run(String... args) {
...
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
configureIgnoreBeanInfo(environment);
// 打印 Banner
Banner printedBanner = printBanner(environment);
...
}
private Banner printBanner(ConfigurableEnvironment environment) {
if (this.bannerMode == Banner.Mode.OFF) {
return null;
}
ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader
: new DefaultResourceLoader(null);
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);
}
printBanner 具體的流程如下:
- 判斷 bannerMode,如果是 OFF,表示不打印。如果是 LOG,表示打印到文件,否則打印到控制臺。
- SpringApplicationBannerPrinter 依據指定位置是是否存在文件,判斷 Banner 類型是文本還是圖片,文本類型使用 ResourceBanner, 圖片類型使用 ImageBanner, 如果都不是,使用 Spring Boot 默認的 SpringBootBanner。
- SpringApplicationBannerPrinter#print 方法調用 Banner 對象的的 printBanner 方法。不同類型的 Banner 的 printBanner 方法實現不同。
如下是 Banner 對象的獲得方法,可以看出,Spring Boot 首先獲得 ImageBanner,然后是 ResourceBanner,需要注意的是,這兩者可以同時存在,此時會一次性打印兩種 Banner。如果都不滿足,還會去獲得 fallbackBanner,這個是用戶自己設定的 Banner,但是我們基本很少使用,大部分情況我們使用了 Spring Boot 內置的 SpringBootBanner。
class SpringApplicationBannerPrinter {
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;
}
private Banner getTextBanner(Environment environment) {
String location = environment.getProperty(BANNER_LOCATION_PROPERTY, DEFAULT_BANNER_LOCATION);
Resource resource = this.resourceLoader.getResource(location);
try {
if (resource.exists() && !resource.getURL().toExternalForm().contains("liquibase-core")) {
return new ResourceBanner(resource);
}
}
catch (IOException ex) {
// Ignore
}
return null;
}
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;
}
}
如何關閉 Spring Boot 的 Banner
從 SpringApplication#printBanner 方法可以看出。當我們將 bannerMode 設置為 Banner.Mode.OFF 的時候,該方法返回 null,也就是此時不會打印 Banner。所以只要設置 bannerMode 為 OFF ,我們就能關閉 Banner 功能。
private Banner printBanner(ConfigurableEnvironment environment) {
if (this.bannerMode == Banner.Mode.OFF) {
return null;
}
...
}
第一種就是在啟動代碼中設置。如下所示。
public static void main(String[] args) {
SpringApplication app
= new SpringApplication(HelloApplication.class);
app.setBannerMode(Banner.Mode.OFF);
app.run(args);
}
第二種是在 application.properties 中配置 spring.main.banner-mode
spring.main.banner-mode=off
這兩種方式都可以關閉 Banner,那當它們同時存在的時候,哪個生效呢?我們可以這樣分析,啟動代碼中調用 setBannerMode 方法,改變了 bannerMode 的值,之后 SpringApplication 對象執行 run 方法,在 run 方法中會解析 application.properties 的值,并將其綁定到對應的 bean,后者覆蓋前者,所以 application.properties 中的配置優先級更高。
自定義文本類型的 Banner
Spring Boot 中自定義文本類型的 Banner 很簡單,我們只要在 resources 下增加一個 banner.txt 就可以了。比如我想讓 Banner 顯示 永不宕機的佛祖雕像。那我就可以在 banner.txt 中增加以下文本。
////////////////////////////////////////////////////////////////////
// _ooOoo_ //
// o8888888o //
// 88" . "88 //
// (| ^_^ |) //
// O = /O //
// ____/`---'____ //
// .' | |// `. //
// / ||| : |||// //
// / _||||| -:- |||||- //
// | | - /// | | //
// | _| ''---/'' | | //
// .-__ `-` ___/-. / //
// ___`. .' /--.-- `. . ___ //
// ."" '< `.____<| >_/___.' >'"". //
// | | : `- `.;` _ /`;.`/ - ` : | | //
// `-. _ __ /__ _/ .-` / / //
// ========`-.____`-.________/___.-`____.-'======== //
// `=---=' //
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //
// 佛祖保佑 永不宕機 永無BUG //
////////////////////////////////////////////////////////////////////
打印結果是這樣的
如果我們想打印彩色的 banner 出來呢?在Spring Boot 中我們可以利用 AnsiColor 類來實現,我們可以在 banner.txt 中使用 AnsiColor 指定后續的文本的顏色。
${AnsiColor.YELLOW}
////////////////////////////////////////////////////////////////////
// _ooOoo_ //
// o8888888o //
// 88" . "88 //
// (| ^_^ |) //
// O = /O //
// ____/`---'____ //
// .' | |// `. //
// / ||| : |||// //
// / _||||| -:- |||||- //
// | | - /// | | //
// | _| ''---/'' | | //
// .-__ `-` ___/-. / //
// ___`. .' /--.-- `. . ___ //
// ."" '< `.____<| >_/___.' >'"". //
// | | : `- `.;` _ /`;.`/ - ` : | | //
// `-. _ __ /__ _/ .-` / / //
// ========`-.____`-.________/___.-`____.-'======== //
// `=---=' //
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //
// 佛祖保佑 永不宕機 永無BUG //
////////////////////////////////////////////////////////////////////
如下是最終的結果
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(LogMessage.format("Banner not printable: %s (%s: '%s')", this.resource, ex.getClass(),
ex.getMessage()), ex);
}
}
protected List< PropertyResolver > getPropertyResolvers(Environment environment, Class< ? > sourceClass) {
List< PropertyResolver > resolvers = new ArrayList< >();
resolvers.add(environment);
resolvers.add(getVersionResolver(sourceClass));
resolvers.add(getAnsiResolver());
resolvers.add(getTitleResolver(sourceClass));
return resolvers;
}
}
我們通過上述代碼來看看,Spring Boot 為什么能打印出帶顏色的 banner, ResourceBanner 將讀取到的文本流轉換為一串字符串。再通過4個解析器,來處理待輸出的 Banner 內容。
四個解析器分別是:
- environment 對應 application.properties 中的配置;
- VersionResolver 解析 spring boot 版本,
- AnsiResolver 解析顏色或字體等樣式配置,
- TitleResolver 解析當前應用的版本,名稱等。
Banner圖在線生成
在線生成 banner 的地址:
- https://www.bootschool.net/ascii
- http://www.network-science.de/ascii/
- http://patorjk.com/software/taag//
- http://www.degraeve.com/img2txt.php
總結
本文主要講述了Banner的輸出打印過程,如何打印個性化的文本 Banner。ImageBanner 沒在本文詳細說明,有興趣的朋友可以查看閱讀源碼,也比較簡單,也有助于你了解 Java 如何實現圖片轉文本的知識點。通過本文的知識點,你也可以根據自己的要求來實現 Banner, 設置為 fallbackBanner, 從而達到完全個性化的自定義 Banner 的效果。
-
接口
+關注
關注
33文章
8575瀏覽量
151021 -
打印
+關注
關注
1文章
64瀏覽量
18714 -
Banner
+關注
關注
0文章
4瀏覽量
7515 -
日志
+關注
關注
0文章
138瀏覽量
10639
發布評論請先 登錄
相關推薦
評論