Banner即横幅的意思,我们在庆祝某些事情时有些显眼包总会是拉个横幅以表明意图。
在我们启动springboot项目时,springboot往往也会打印出一个默认的横幅,该横幅中包含了一些信息如springboot版本号等,如下图所示。
既然有默认的,当然springboot也允许我们对banner进行自定义设置,如将banner设置为文本、图片,甚至关闭banner的输出。如下所示
是否略显炫酷?下面我们来介绍如何自定义banner,并通过源码进行分析。
本演示项目的环境如下:
当我们需要自定义文本banner时,只需要在类路径classpath下新建一个文件,文件名为banner.txt
(springboot默认),然后编辑作为banner的文本即可,如下所示。
此时输出如下
如果不使用springboot默认的banner文件名,则需要通过在配置文件中配置spring.banner.location
,如下所示
如果文件的编码不是UTF-8,则可以在配置文件中通过spring.banner.charset
配置字符集,如下所示
spring:
banner:
location: fozuBanner.txt
charset: UTF-32
除了文本banner外,springboot还允许我们自定义图片banner。并且如果两种banner同时存在,则先输出图片banner、再输出文本banner。
默认地,springboot将从classpath类路径下获取banner.gif
、banner.jpg
、banner.png
作为图片banner。当然也可以通过在配置文件中配置spring.banner.image.location
来指定图片的位置。
在输出图片banner时,springboot将会把图片转化成ASCII艺术画输出,而非无脑式地将图片输出。
如下图所示,我们在类路径下添加图片,并将其命名为banner.png
。
启动项目后的输出如下
springboot允许我们在banner中使用${}
格式的占位符,但仅限于文本banner。内置的占位符有应用版本、springboot版本、字体样式、应用名。下面我们一一介绍。
应用版本
占位符为${application.version}
或 ${application.formatted-version}
。
springboot允许我们在文本banner中添加应用版本号。通过在文本banner中添加${application.version}
或${application.formatted-version}
时,springboot从MANIFEST.MF
中读取Implementation-Version
的值。
例如,MANIFEST.MF
中Implementation-Version
的值为1.0.0
,则${application.version}
得到的值为1.0.0
;${application.formatted-version}
的值为v1.0.0
,多了个前缀v
。
注意:该信息只有通过Spring Boot launchers
方式启动时才会生效。
springboot版本
占位符为${spring-boot.version}
或 ${spring-boot.formatted-version}
。
获取当前项目使用的springboot的版本号,同样的,后者会给前者获取的值添加前缀v
。如下图所示
字体样式
占位符为${AnsiColor.NAME}
、${AnsiBackground.NAME}
、${AnsiStyle.NAME}
。
当我们使用${AnsiColor.RED}
时,打印的字体将变成红色;使用${AnsiBackground.YELLOW}
时,背景颜色将变成黄色;使用${AnsiStyle.BOLD}
时,将打印粗体。如下图所示
应用名
占位符为${application.title}
。
springboot允许我们在文本banner中添加应用名称。通过在文本banner中添加${application.title}
时,springboot从MANIFEST.MF
中读取Implementation-Title
的值。
例如,MANIFEST.MF
中Implementation-Title
的值为MyApp
,则Implementation-Title
得到的值为MyApp
。
注意:该信息只有通过Spring Boot launchers
方式启动时才会生效。
我们介绍了文本banner、图片banner后,接下来介绍如何关闭banner,关闭bannner后在项目启动时便不再输出banner。
方法当然也很简单,对于关闭bannner功能,springboot在SpringApplication
类中提供了响应的方法setBannerMode()
来关闭banner。下面对我们的主启动类进行修改。
修改前
public static void main(String[] args){
SpringApplication.run(BannerApplication.class, args);
}
修改后
public static void main(String[] args){
SpringApplication springApplication = new SpringApplication(BannerApplication.class);
// 将bannner的模式设置为off,即关闭
springApplication.setBannerMode(Banner.Mode.OFF);
springApplication.run(args);
}
启动项目
我们知道springboot是通过SpringApplication
类中的run()
方法启动的,在该方法中调用printBanner()
方法打印banner,如下图所示
下面我们进入该方法
在printBanner()
方法中我们首先看到对bannerMode
的判断,如果是OFF
,则直接返回null
。
if (this.bannerMode == Banner.Mode.OFF) {
return null;
}
而前面我们在演示的时候提到过,springboot提供了对应的方法。
public void setBannerMode(Banner.Mode bannerMode) {
this.bannerMode = bannerMode;
}
我们看一下Banner.Mode
为何物?
Banner.Mode
表示为Banner
的模式,springboot提供了三种模式:OFF关闭、CONSOLE控制台、LOG日志文件。
springboot默认的banner模式为CONSOLE控制台。
在printBanner()
方法中创建了banner打印器实例,代码如下所示
private Banner printBanner(ConfigurableEnvironment environment) {
// ...
SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);
// ...
}
在调用构造方法实例化打印器时,传入资源加载器 和 兜底banner,兜底banner的含义为如果项目中没有指定的文本banner或图片banner时,则使用兜底banner,如果兜底banner依然不存在,最后才使用springboot默认banner。
springboot提供了设置兜底banner的方法:
public void setBanner(Banner banner) {
this.banner = banner;
}
与设置banner模式相同,在主启动类中设置一个自定义的banner,该自定义banner必须实现Banner
接口的printBanner()
方法。
public static void main(String[] args){
SpringApplication springApplication = new SpringApplication(BannerApplication.class);
springApplication.setBanner(new CustomBanner());
springApplication.run(args);
}
在完成banner打印器的实例化以后,就开始调用打印器的print()
方法对banner进行打印了,且打印器提供了两个重载的的print()
方法,分别用于日志模式和控制台模式。这两个重载方法的不同之处在第三个参数。如下所示
private Banner printBanner(ConfigurableEnvironment environment) {
// ...
if (this.bannerMode == Mode.LOG) {
return bannerPrinter.print(environment, this.mainApplicationClass, logger);
}
return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}
这两个重载方法的基本逻辑是相同的,即获取banner、打印banner、返回结果。我们以控制台模式的print()
方法为例。
Banner print(Environment environment, Class<?> sourceClass, PrintStream out) {
// 获取banner
Banner banner = getBanner(environment);
// 打印banner
banner.printBanner(environment, sourceClass, out);
// 返回结果
return new PrintedBanner(banner, sourceClass);
}
我们进入getBanner()
方法查看其实现
该实现逻辑也很清晰,就是先后获取图片banner添加到banners中,如果banners中存在banner,则返回;否则返回兜底banner;如果兜底banner还不存在,则返回默认banner。
获取图片banner
获取到的图片banner用ImageBanner
对象表示。
获取文本banner
获取到的文本banner用ResourceBanner
对象表示。
兜底banner
前面我们在介绍banner打印器时,已经详细介绍过了。
默认banner
默认banner使用常量DEFAULT_BANNER
表示为SpringBootBanner
对象。如下所示
进入SpringBootBanner
后发现发现我们再熟悉不过的默认banner,原来藏在这里。
前面在getBanner()
方法中获取到的banner集合被添加到Bannners
中,注意一下,类Banners
是Banner
的子类,在它实现的printBanner()
方法中,通过遍历内部的banner集合并调用printBanner()
方法对不同的banner进行打印。
下面我们对不同类型bannner的打印逻辑进行分析
图片banner
图片banner被封装在ImageBanner
对象中,在打印图片banner时,会对java.awt.headless
的配置进行处理,然后再调用其重载方法真正地输出图片banner。
下面我们点击重载printBanner()
方法,查看其真正输出图片banner的逻辑。
在该方法中,从配置中获取图片banner的宽高等基本样式,然后将其输出,在输出过程中将图片转为ASCII艺术图。
文本banner
文本banner被封装在ResourceBanner
对象中,我们进入该类的printBanner()
方法。
该方法逻辑为将文本banner按照配置文件中spring.banner.charset
指定的字符集转换为对应的banner字符串;然后获取用于解析${}
形式的占位符的解析器集合,利用解析器处理banner字符串中的占位符。最后将处理后的banner字符串输出。
下面我们看一下有哪些占位符解析器
从源码中,我们看到,有处理版本号的解析器、文本格式的解析器、应用标题的解析器。
获取版本号占位符的解析器是通过getVersionResolver()
方法完成的,如下图所示,可以看到springboot内置给我们获取应用版本号和springboot版本号的占位符。
获取文本格式占位符的解析器是通过getAnsiResolver()
方法完成的。如下图所示,可以看到内置了多种设置文本格式的方式。
下面我们以设置文本格式和文本颜色为例
文本格式
文本格式通过AnsiStyle
设置,所以我们点击其对应的AnsiStyle
类,发现我们可以设置的文本格式如下
文本颜色
文本颜色通过AnsiColor
设置,所以我们点击其对应的AnsiColor
类,发现我们可以设置的文本颜色如下
获取应用标题占位符的解析器是通过getTitleResolver()
方法完成的。如下图所示。
纸上得来终觉浅,绝知此事要躬行。
————————————————我是万万岁,我们下期再见————————————————