SpringBoot之Banner介绍

一、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         
                                               .&&    .&&                     
                                                 &&&&&&&             

三、修改默认文件名

bannerSpringBoot 默认名,我们也可以自定义文件名称。

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 文件中。
posted @ 2020-03-17 08:32  MarkLogZhu  阅读(1261)  评论(0编辑  收藏  举报