一、Banner 介绍
1.1 Banner 是什么?
. ____ _ __ _ _
/\ / ___'_ __ _ _(_)_ __ __ _
( ( )\___ | '_ | '_| | '_ / _` |
\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.2.5.RELEASE)
如上图所示的,是我们每次启动 SpringBoot 项目时就会在控制器输出的内容,这个就是 **Banner **。
1.2 banner的输出模式
* LOG:将 banner 信息输出到日志文件。
* CONSOLE:将 banner 信息输出到控制台。
* OFF:禁用 banner 的信息输出。
二、自定义 Banner
2.1 文本形式 banner
在 resources
目录下新建 banner.txt
文件,内容如下:
/*
_ooOoo_
o8888888o
88" . "88
(| -_- |)
O = /O
____/`---'\____
.' \| |// `.
/ \||| : |||//
/ _||||| -:- |||||-
| | \ - /// | |
| \_| ''---/'' | |
.-\__ `-` ___/-. /
___`. .' /--.-- `. . __
."" '< `.___\_<|>_/___.' >'"".
| | : `- \`.;` _ /`;.`/ - ` : | |
`-. \_ __ /__ _/ .-` / /
======`-.____`-.___\_____/___.-`____.-'======
`=---='
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
佛祖保佑 永无BUG
*/
启动项目,观察控制台输出:
/*
_ooOoo_
o8888888o
88" . "88
(| -_- |)
O = /O
____/`---'\____
.' \| |// `.
/ \||| : |||//
/ _||||| -:- |||||-
| | \ - /// | |
| \_| ''---/'' | |
.-\__ `-` ___/-. /
___`. .' /--.-- `. . __
."" '< `.___\_<|>_/___.' >'"".
| | : `- \`.;` _ /`;.`/ - ` : | |
`-. \_ __ /__ _/ .-` / /
======`-.____`-.___\_____/___.-`____.-'======
`=---='
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
佛祖保佑 永无BUG
*/
可以看到 Banner 样式已经改变了。
2.2 图片样式 Banner
在 resources
下创建 banner.jpg
文件
启动项目,查看控制台输出:
&&&&&&&&&& &&
&&& : &&
&&* &&
&&& 8888888* :****** &&&&&&&&: && 8888888
&&& &&&&&&&& 88& 888 ** **: && &&: && 88: .88
&& && 888 88 *** ** &&. &&: && :8888888
&&& &&& 88 88 *** *** &&& &&: && 88.
8&&&&&&&&&& .88888888 ********* &&&&&&&&&: && 88888888
:&&. .8& .** o& &&* .88
.&& .&&
&&&&&&&
三、修改默认文件名
banner 是 SpringBoot 默认名,我们也可以自定义文件名称。
3.1 通过配置文件指定文件名
# 文本形式
spring.banner.location=icon.txt
# 图片形式
spring.banner.image.location=icon.jpg
3.2 通过代码形式指定文件名
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(SpringbootApplication.class);
springApplication.setBanner(new ResourceBanner(new ClassPathResource("icon.txt")));
springApplication.run();
}
四、修改模式
4.1 通过配置文件修改模式
spring.main.banner-mode=off
4.2 通过代码形式修改模式
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(SpringbootApplication.class);
springApplication.setBannerMode(Banner.Mode.LOG);
springApplication.run();
}
五、Banner 输出原理
进入 run
方法:发现有个 printBanner
方法:
public ConfigurableApplicationContext run(String... args) {
......
try{
......
Banner printedBanner = printBanner(environment);
......
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
......
}catch (Throwable ex) {
......
}
}
我们看下它的实现:
private Banner printBanner(ConfigurableEnvironment environment) {
// 判断 banner 模式
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);
}
进入 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
方法:
private Banner getBanner(Environment environment) {
Banners banners = new Banners();
// 获取图片 banner
banners.addIfNotNull(getImageBanner(environment));
// 获取文本 banner
banners.addIfNotNull(getTextBanner(environment));
// 判断是否存在一个 banner
if (banners.hasAtLeastOneBanner()) {
return banners;
}
// 后备 banner
if (this.fallbackBanner != null) {
return this.fallbackBanner;
}
// 默认 banner
return DEFAULT_BANNER;
}
查看两个获取 Banne
的方法
// 默认文件名
static final String BANNER_LOCATION_PROPERTY = "spring.banner.location";
// 默认图片名
static final String BANNER_IMAGE_LOCATION_PROPERTY = "spring.banner.image.location";
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;
}
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;
}
图片打印实现类 ImageBanner:
@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(LogMessage.format("Image banner not printable: %s (%s: '%s')", 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);
BitDepth bitDepth = getBitDepthProperty(environment);
PixelMode pixelMode = getPixelModeProperty(environment);
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, bitDepth, pixelMode, out);
sleep(frames[i].getDelayTime());
}
}
重点关注 readFrames 方法:
private Frame[] readFrames(int width, int height) throws IOException {
try (InputStream inputStream = this.image.getInputStream()) {
try (ImageInputStream imageStream = ImageIO.createImageInputStream(inputStream)) {
return readFrames(width, height, imageStream);
}
}
}
private Frame[] readFrames(int width, int height, ImageInputStream stream) throws IOException {
Iterator<ImageReader> readers = ImageIO.getImageReaders(stream);
Assert.state(readers.hasNext(), "Unable to read image banner source");
ImageReader reader = readers.next();
try {
ImageReadParam readParam = reader.getDefaultReadParam();
reader.setInput(stream);
int frameCount = reader.getNumImages(true);
Frame[] frames = new Frame[frameCount];
for (int i = 0; i < frameCount; i++) {
frames[i] = readFrame(width, height, reader, i, readParam);
}
return frames;
}
finally {
reader.dispose();
}
}
六、推荐在线制作 banner 的网站
优点:
- 切换字体以及输入文字的时候, 会自动输出 banner 字样。
- 在输出框中,是可编辑的,可以自己添加版本号,作者简介信息等。
缺点:
- 不能下载 txt。
- 结果内容只有选中后,手动复制粘贴到 banner.txt 文件中。