SpringBoot啟動Banner設定
本文為原創,轉載請標明出處!
我們都知道SpringBoot在啟動時會列印一個Banner,就是一個SpringBoot的標誌,如下:
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.0.5.RELEASE)
那麼這個標誌是怎麼打印出來的,如何修改呢?今天就從原始碼級別進行相關解析和修改處理。
1、原始碼分析
啟動SpringBoot的時候,常用的方式是使用SpringApplication.run(xxx.class, args);
當然還有幾種其他的啟動方式(後面在說明修改Banner時會涉及),但無論哪種啟動方式,最後都會執行SpringApplication的run方法,下面是這個run方法的原始碼:
public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment); // 此處列印 ......
上面加註釋的那部分就是其列印Banner的程式碼,我們再開啟這個printBanner()看下原始碼,如下:
private Banner printBanner(ConfigurableEnvironment environment) { 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); }
其中涉及到了一個BannerMode的概念,這個是一個列舉,描述的是列印Banner的模式:
enum Mode {
/**
* Disable printing of the banner.
*/
OFF,
/**
* Print the banner to System.out.
*/
CONSOLE,
/**
* Print the banner to the log file.
*/
LOG
}
原始碼已經註釋的非常清晰,有三種模式,此處不再贅述。
書接上文,我們發現繞過一些判斷的方法,最後都是執行的bannerPrinter.print方法,這個bannerPrinter是一個類:SpringApplicationBannerPrinter,我們開啟這部分原始碼繼續分析:
public Banner print(Environment environment, Class<?> sourceClass, PrintStream out) {
Banner banner = getBanner(environment);
banner.printBanner(environment, sourceClass, out);
return new PrintedBanner(banner, sourceClass);
}
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;
}
處理邏輯其實很簡單,就是首先獲取當前環境的Banner(Banner其實是一個介面),然後執行這個banner的printBanner方法。SpringBoot在啟動的時候使用的是DEFAULT_BANNER,DEFAULT_BANNER對應的類是:
private static final Banner DEFAULT_BANNER = new SpringBootBanner();
到這裡我們已經很清楚了,其實SpringBoot在啟動時呼叫的是SpringBootBanner這個類,下面分析一下這個類。
首先是SpringBootBanner的原始碼:
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();
}
}
哈哈,是不是很開心的看到了SpringBoot啟動時列印的圖示?
從上面我們可以很清晰的看出:
1)這個類實現了Banner這個介面;
2)它列印了Banner資訊;
在這裡我們其實也學習到了另外的一個知識:那就是獲取版本號的處理。
String version = SpringBootVersion.getVersion();
我們看下SpringBootVersion的原始碼,我們發現其實它還是呼叫的JDK提供的package來獲取的:
public final class SpringBootVersion {
private SpringBootVersion() {
}
/**
* Return the full version string of the present Spring Boot codebase, or {@code null}
* if it cannot be determined.
* @return the version of Spring Boot or {@code null}
* @see Package#getImplementationVersion()
*/
public static String getVersion() {
Package pkg = SpringBootVersion.class.getPackage();
return (pkg != null) ? pkg.getImplementationVersion() : null;
}
}
2、修改Banner
下面我們自己定義一個圖示進行列印:
首先定義一個類:MyBanner,該類需要實現Banner介面,我們參考SpringBootBanner的處理方式,原始碼如下:
public class MyBanner implements Banner {
private static final String[] BANNER = {
"-------------------------",
"| My Name is SpringBoot |",
"-------------------------"};
@Override
public void printBanner(Environment environment, Class<?> sourceClass, PrintStream printStream) {
for (String line : BANNER) {
printStream.println(line);
}
printStream.println();
}
}
下面我們修改SpringBoot的啟動方式,前面我們已經提到了SpringBoot最基本的啟動方式:
SpringApplication.run(BootStart.class, args);
還有兩種常用的啟動方式:
SpringApplication springApplication = new SpringApplication(BootStart.class);
springApplication.run(args);
和
new SpringApplicationBuilder().sources(BootStart.class).run(args);
而我們修改Banner必須依賴於下面的這兩種方式。
1)第一種修改,原始碼如下:
SpringApplication springApplication = new SpringApplication(BootStart.class);
springApplication.setBanner(new MyBanner()); // 設定為自定義的Banner
springApplication.run(args);
2)第二種修改,原始碼如下:
new SpringApplicationBuilder().banner(new MyBanner()).sources(BootStart.class).run(args);
通過上述兩種修改都可以達到我們想要的效果:
為什麼可以達到這個效果呢?答案還是在原來我們看到的SpringApplicationBannerPrinter獲取Banner的方法中,原始碼:
public Banner print(Environment environment, Class<?> sourceClass, PrintStream out) {
Banner banner = getBanner(environment);
banner.printBanner(environment, sourceClass, out);
return new PrintedBanner(banner, sourceClass);
}
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;
}
這裡面有一個fallbackBanner,而這個fallbackBanner恰好就是我們通過setBanner方法設定的,由下面幾段程式碼可以清晰的看出,其實這些程式碼原先都看過:
public void setBanner(Banner banner) {
this.banner = banner;
}
private Banner printBanner(ConfigurableEnvironment environment) {
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); // 就是這個banner
if (this.bannerMode == Mode.LOG) {
return bannerPrinter.print(environment, this.mainApplicationClass, logger);
}
return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}
SpringApplicationBannerPrinter(ResourceLoader resourceLoader, Banner fallbackBanner) {
this.resourceLoader = resourceLoader;
this.fallbackBanner = fallbackBanner; // 就是setBanner中的那個Banner
}
這樣就一目瞭然了~~~~~~