文稿原地址: https://www.yuque.com/wbf1013/mglhnr/ah83wl
大家好,我是指北君。
前言 {#前言}
今天,我给大家来讲讲在 Spring Boot 项目中,自定义 banner 的事情。有些新入门的朋友可能会不知道 banner 是什么?它在哪里? 我在哪里见过它吗? 这3连门是不是很有意思。 我们今天所说的 banner 如下图所示,想必大家在启动 Spring Boot 项目的时候,大家都见过吧。
大家可能都见过永不宕机的佛祖的 banner 图片吧。 下图,大家应该都很熟悉吧。
今天我就来带大家来看看源码,看看这个 banner 到底是怎么实现的。
Spring Boot 内置 3 种 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 表示输出到控制台,也是我们比较常见的方式。
|---------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9
| @FunctionalInterface public interface Banner { void printBanner(Environment environment, Class<?> sourceClass, PrintStream out); enum Mode { OFF, CONSOLE, LOG } } |
Banner 打印流程 {#banner-打印流程}
从 SpringApplication 类的 run 方法可以看出,printBanner 方法在 prepareEnvironment 之后,这是因为 application.properties 中有一些关于 Banner 的配置项。需要先解析 application.properties 的值,并将其绑定到对应的 bean 之后,再进行后续的操作。
|---------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| 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。
|---------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| 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 {#如何关闭-spring-boot-的-banner}
从 SpringApplication#printBanner 方法可以看出。当我们将 bannerMode 设置为 Banner.Mode.OFF 的时候,该方法返回 null,也就是此时不会打印 Banner。所以只要设置 bannerMode 为 OFF ,我们就能关闭 Banner 功能。
|---------------------|----------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6
| private Banner printBanner(ConfigurableEnvironment environment) { if (this.bannerMode == Banner.Mode.OFF) { return null; } ... } |
第一种就是在启动代码中设置。如下所示。
|---------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6
| 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
|-----------|-----------------------------|
| 1
| spring.main.banner-mode=off |
这两种方式都可以关闭 Banner,那当它们同时存在的时候,哪个生效呢?我们可以这样分析,启动代码中调用 setBannerMode 方法,改变了 bannerMode 的值,之后 SpringApplication 对象执行 run 方法,在 run 方法中会解析 application.properties 的值,并将其绑定到对应的 bean,后者覆盖前者,所以 application.properties 中的配置优先级更高。
自定义文本类型的 Banner {#自定义文本类型的-banner}
Spring Boot 中自定义文本类型的 Banner 很简单,我们只要在 resources 下增加一个 banner.txt 就可以了。比如我想让 Banner 显示 永不宕机的佛祖雕像。那我就可以在 banner.txt 中增加以下文本。
</table>
</div>
</div>
Banner 打印流程 {#banner-打印流程}
从 SpringApplication 类的 run 方法可以看出,printBanner 方法在 prepareEnvironment 之后,这是因为 application.properties 中有一些关于 Banner 的配置项。需要先解析 application.properties 的值,并将其绑定到对应的 bean 之后,再进行后续的操作。
<div class="language-java highlighter-rouge">
<div class="highlight">
<table style="margin: 0px">
<tbody>
<tr>
<td class="gutter"><pre>1<br>2<br>3<br>4<br>5<br>6<br>7<br>8<br>9<br>10<br>11<br>12<br>13<br>14<br>15<br>16<br>17<br>18<br>19<br>20<br>21</pre></td>
<td class="code"><pre class="highlight"><code><span class="kd">public</span> <span class="nc">ConfigurableApplicationContext</span> <span class="nf">run</span><span class="o">(</span><span class="nc">String</span><span class="o">...</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
<span class="o">...</span>
<span class="nc">ApplicationArguments</span> <span class="n">applicationArguments</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">DefaultApplicationArguments</span><span class="o">(</span><span class="n">args</span><span class="o">);</span>
<span class="nc">ConfigurableEnvironment</span> <span class="n">environment</span> <span class="o">=</span> <span class="n">prepareEnvironment</span><span class="o">(</span><span class="n">listeners</span><span class="o">,</span> <span class="n">bootstrapContext</span><span class="o">,</span> <span class="n">applicationArguments</span><span class="o">);</span>
<span class="n">configureIgnoreBeanInfo</span><span class="o">(</span><span class="n">environment</span><span class="o">);</span>
<span class="c1">// 打印 Banner</span>
<span class="nc">Banner</span> <span class="n">printedBanner</span> <span class="o">=</span> <span class="n">printBanner</span><span class="o">(</span><span class="n">environment</span><span class="o">);</span>
<span class="o">...</span>
} 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); }
</table>
</div>
</div>
printBanner 具体的流程如下:
<ol>
<li>判断 bannerMode,如果是 OFF,表示不打印。如果是 LOG,表示打印到文件,否则打印到控制台。</li>
<li>SpringApplicationBannerPrinter 依据指定位置是是否存在文件,判断 Banner 类型是文本还是图片,文本类型使用 ResourceBanner, 图片类型使用 ImageBanner, 如果都不是,使用 Spring Boot 默认的 SpringBootBanner。</li>
<li>SpringApplicationBannerPrinter#print 方法调用 Banner 对象的的 printBanner 方法。不同类型的 Banner 的 printBanner 方法实现不同。</li>
</ol>
如下是 Banner 对象的获的方法,可以看出,Spring Boot 首先获得 ImageBanner,然后是 ResourceBanner,需要注意的是,这两者可以同时存在,此时会一次性打印两中 Banner。如果都不满足,还会去获得 fallbackBanner,这个是用户自己设定的 Banner,但是我们基本很少使用,大部分情况我们使用了 Spring Boot 内置的 SpringBootBanner。
<div class="language-plaintext highlighter-rouge">
<div class="highlight">
<table style="margin: 0px">
<tbody>
<tr>
<td class="gutter"><pre>1<br>2<br>3<br>4<br>5<br>6<br>7<br>8<br>9<br>10<br>11<br>12<br>13<br>14<br>15<br>16<br>17<br>18<br>19<br>20<br>21<br>22<br>23<br>24<br>25<br>26<br>27<br>28<br>29<br>30<br>31<br>32<br>33<br>34<br>35<br>36<br>37<br>38<br>39<br>40<br>41</pre></td>
<td class="code"><pre class="highlight"><code>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;
}
}
</table>
</div>
</div>
如何关闭 Spring Boot 的 Banner {#如何关闭-spring-boot-的-banner}
从 SpringApplication#printBanner 方法可以看出。当我们将 bannerMode 设置为 Banner.Mode.OFF 的时候,该方法返回 null,也就是此时不会打印 Banner。所以只要设置 bannerMode 为 OFF ,我们就能关闭 Banner 功能。
<div class="language-java highlighter-rouge">
<div class="highlight">
<table style="margin: 0px">
<tbody>
<tr>
<td class="gutter"><pre>1<br>2<br>3<br>4<br>5<br>6</pre></td>
<td class="code"><pre class="highlight"><code><span class="kd">private</span> <span class="nc">Banner</span> <span class="nf">printBanner</span><span class="o">(</span><span class="nc">ConfigurableEnvironment</span> <span class="n">environment</span><span class="o">)</span> <span class="o">{</span>
if (this.bannerMode == Banner.Mode.OFF) { return null; } ... }
</table>
</div>
</div>
第一种就是在启动代码中设置。如下所示。
<div class="language-java highlighter-rouge">
<div class="highlight">
<table style="margin: 0px">
<tbody>
<tr>
<td class="gutter"><pre>1<br>2<br>3<br>4<br>5<br>6</pre></td>
<td class="code"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
<span class="nc">SpringApplication</span> <span class="n">app</span>
<span class="o">=</span> <span class="k">new</span> <span class="nc">SpringApplication</span><span class="o">(</span><span class="nc">HelloApplication</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
<span class="n">app</span><span class="o">.</span><span class="na">setBannerMode</span><span class="o">(</span><span class="nc">Banner</span><span class="o">.</span><span class="na">Mode</span><span class="o">.</span><span class="na">OFF</span><span class="o">);</span>
<span class="n">app</span><span class="o">.</span><span class="na">run</span><span class="o">(</span><span class="n">args</span><span class="o">);</span>
}
</table>
</div>
</div>
第二种是在 application.properties 中配置 spring.main.banner-mode
<div class="language-java highlighter-rouge">
<div class="highlight">
<table style="margin: 0px">
<tbody>
<tr>
<td class="gutter"><pre>1</pre></td>
<td class="code"><pre class="highlight"><code><span class="n">spring</span><span class="o">.</span><span class="na">main</span><span class="o">.</span><span class="na">banner</span><span class="o">-</span><span class="n">mode</span><span class="o">=</span><span class="n">off</span>
</table>
</div>
</div>
这两种方式都可以关闭 Banner,那当它们同时存在的时候,哪个生效呢?我们可以这样分析,启动代码中调用 setBannerMode 方法,改变了 bannerMode 的值,之后 SpringApplication 对象执行 run 方法,在 run 方法中会解析 application.properties 的值,并将其绑定到对应的 bean,后者覆盖前者,所以 application.properties 中的配置优先级更高。
自定义文本类型的 Banner {#自定义文本类型的-banner}
Spring Boot 中自定义文本类型的 Banner 很简单,我们只要在 resources 下增加一个 banner.txt 就可以了。比如我想让 Banner 显示 永不宕机的佛祖雕像。那我就可以在 banner.txt 中增加以下文本。
<div class="language-java highlighter-rouge">
<code></code>
<div class="highlight">
<code>.;`\ _ /`;.`/ - ` : | | //
// \ \ -. \_ __\ /__ _/ .-
/ / //
// ========-.____
-._ / .-____.-'======== // </span> <span class="c1">//
=---=' //
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //
// 佛祖保佑 永不宕机 永无BUG //
////////////////////////////////////////////////////////////////////
</div>
</div></pre></td>
</tr>
</tbody>
|------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| //////////////////////////////////////////////////////////////////// // ooOoo // // o8888888o // // 88" . "88 // // (| ^_^ |) // // O\ = /O // // ____/---'\____ // // .' \| |//
. // // / ||| : |||// \ // // / ||||| -:- |||||- \ // // | | \ - /// | | // // | _| ''---/'' | | // // \ .-_ -
___/-. / // // ___. .' /--.--\
. . ___ // // ."" '< .___\_<|>_/___.' >'"". // // | | :
- 文稿原地址: https://www.yuque.com/wbf1013/mglhnr/ah83wl 大家好,我是指北君。 ### 前言 {#前言} 今天,我给大家来讲讲在 Spring Boot 项目中,自定义 banner 的事情。有些新入门的朋友可能会不知道 banner 是什么?它在哪里? 我在哪里见过它吗? 这3连门是不是很有意思。 我们今天所说的 banner 如下图所示,想必大家在启动 Spring Boot 项目的时候,大家都见过吧。 大家可能都见过永不宕机的佛祖的 banner 图片吧。 下图,大家应该都很熟悉吧。 今天我就来带大家来看看源码,看看这个 banner 到底是怎么实现的。 ### Spring Boot 内置 3 种 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 表示输出到控制台,也是我们比较常见的方式。
1 |
|