Spring Boot banner详解

2023-10-16 11:20:48 浏览数 (1)

Spring Boot 3.x系列文章

  1. Spring Boot 2.7.8 中文参考指南(一)
  2. Spring Boot 2.7.8 中文参考指南(二)-Web
  3. Spring Boot 源码阅读初始化环境搭建
  4. Spring Boot 框架整体启动流程详解
  5. Spring Boot 系统初始化器详解
  6. Spring Boot 监听器详解
  7. Spring Boot banner详解

自定义banner

Spring Boot 默认打印的banner是这样的,Java工程师看都看腻了。

一般的公司如果有自己脚手架,都会选择自定义banner,放一个公司Logo或者框架别名。

简易版banner

首先生成一个自己的banner,比如我生成的

生成的网站很多,可以用"banner 生成器"自行搜索

把生成的内容copy到txt中,命名为"banner.txt"(UTF-8),然后放到resources下。

启动Spring Boot 即可看到效果。

自定义banner路径

上述的banner.txt 只能放在resources根目录下,不能在resources子目录或其他的目录,使用spring.banner.location指定该文件的路径,如果该文件不是UTF-8编码,使用spring.banner.charset指定文件编码,比如我将文件放到resources的子目录static中。

自定义banner 样式

光一个Logo也还是太单调,如果能再打印个Spring Boot 版本、应用程序版本就更好了,Spring Boot 都给我们提供了相关变量,可以在banner.txt中使用。

  • ${application.version} 应用程序版本
  • ${application.formatted-version} 格式化的应用程序版本,前缀是v
  • ${spring-boot.version} Spring Boot框架版本
  • ${spring-boot.formatted-version} 格式化的Spring Boot框架版本,前缀是v
  • {Ansi.NAME} (or {AnsiColor.NAME}, {AnsiBackground.NAME}, {AnsiStyle.NAME}) 给文字加颜色,加背景,加样式,NAME的值可以在这里获取ansi
  • ${application.title} 应用程序的标题

如图我给banner 加上了颜色,加上了版本

配置如下:

代码语言:javascript复制
${AnsiColor.GREEN}
  /$$$$$$   /$$$$$$  /$$$$$$$  /$$   /$$       /$$$$$$ /$$$$$$$$ /$$$$$$   /$$$$$$  /$$     /$$ /$$$$$$   /$$$$$$  /$$     /$$
 /$$__  $$ /$$__  $$| $$__  $$| $$$ | $$      |_  $$_/|__  $$__//$$__  $$ /$$__  $$|  $$   /$$//$$__  $$ /$$__  $$|  $$   /$$/
| $$  __/| $$  __/| $$   $$| $$$$| $$        | $$     | $$  | $$  __/| $$   $$   $$ /$$/| $$  __/| $$   $$   $$ /$$/
| $$      |  $$$$$$ | $$  | $$| $$ $$ $$ /$$$$$$| $$     | $$  |  $$$$$$ | $$$$$$$$    $$$$/ |  $$$$$$ | $$$$$$$$    $$$$/
| $$       ____  $$| $$  | $$| $$  $$$$|______/| $$     | $$   ____  $$| $$__  $$     $$/   ____  $$| $$__  $$     $$/
| $$    $$ /$$   $$| $$  | $$| $$  $$$        | $$     | $$   /$$   $$| $$  | $$    | $$    /$$   $$| $$  | $$    | $$
|  $$$$$$/|  $$$$$$/| $$$$$$$/| $$   $$       /$$$$$$   | $$  |  $$$$$$/| $$  | $$    | $$   |  $$$$$$/| $$  | $$    | $$
 ______/  ______/ |_______/ |__/  __/      |______/   |__/   ______/ |__/  |__/    |__/    ______/ |__/  |__/    |__/
${AnsiColor.DEFAULT}
::Spring Boot Version: ${AnsiColor.RED}${spring-boot.formatted-version}${AnsiColor.DEFAULT}
::Application Version: ${AnsiColor.RED}${application.formatted-version}${AnsiColor.DEFAULT}

  • 如果颜色不起作用,那就是需要开启一下:spring.output.ansi.enabled=always
  • ${AnsiColor.DEFAULT} 是将颜色重置,防止前面的颜色影响下面的
  • {application.formatted-version} 、 {application.version}、
  • banner.txt 中可以配置环境变量environment中的任何键值

使用图片做banner

在Spring Boot 3.x版本中已经不被支持

编码方式定义banner

自定义一个CustomBanner类,实现Banner接口,如:

代码语言:javascript复制
import org.springframework.boot.Banner;
import org.springframework.core.env.Environment;

import java.io.PrintStream;

public class CustomBanner implements Banner {

    public static final String BANNER = "  /$$$$$$   /$$$$$$  /$$$$$$$  /$$   /$$       /$$$$$$ /$$$$$$$$ /$$$$$$   /$$$$$$  /$$     /$$ /$$$$$$   /$$$$$$  /$$     /$$n"  
            " /$$__  $$ /$$__  $$| $$__  $$| $$$ | $$      |_  $$_/|__  $$__//$$__  $$ /$$__  $$|  $$   /$$//$$__  $$ /$$__  $$|  $$   /$$/n"  
            "| $$  \__/| $$  \__/| $$  \ $$| $$$$| $$        | $$     | $$  | $$  \__/| $$  \ $$ \  $$ /$$/| $$  \__/| $$  \ $$ \  $$ /$$/ n"  
            "| $$      |  $$$$$$ | $$  | $$| $$ $$ $$ /$$$$$$| $$     | $$  |  $$$$$$ | $$$$$$$$  \  $$$$/ |  $$$$$$ | $$$$$$$$  \  $$$$/  n"  
            "| $$       \____  $$| $$  | $$| $$  $$$$|______/| $$     | $$   \____  $$| $$__  $$   \  $$/   \____  $$| $$__  $$   \  $$/   n"  
            "| $$    $$ /$$  \ $$| $$  | $$| $$\  $$$        | $$     | $$   /$$  \ $$| $$  | $$    | $$    /$$  \ $$| $$  | $$    | $$    n"  
            "|  $$$$$$/|  $$$$$$/| $$$$$$$/| $$ \  $$       /$$$$$$   | $$  |  $$$$$$/| $$  | $$    | $$   |  $$$$$$/| $$  | $$    | $$    n"  
            " \______/  \______/ |_______/ |__/  \__/      |______/   |__/   \______/ |__/  |__/    |__/    \______/ |__/  |__/    |__/    n";

    @Override
    public void printBanner(Environment environment, Class<?> sourceClass, PrintStream printStream) {
        printStream.println(BANNER);
    }
}

SpringApplication 中配置banner

代码语言:javascript复制
SpringApplication springApplication = new SpringApplication(SpringBootDemoApplication.class);
springApplication.setBanner(new CustomBanner());

如果在类路径中存在banner.txt 在会优先使用banner.txt

禁用banner

Spring Boot 提供了spring.main.banner-mode 配置,OFF-关闭banner打印,CONSOLE-使用System.out打印banner,log文件不会记录,LOG-打印到log文件

另外同样可以用springApplication.setBannerMode(Banner.Mode.OFF);方式设置banner

加载打印原理

在之前的《Spring Boot 框架整体启动流程详解》中,我们看到有一步是

代码语言:javascript复制
//打印banner
Banner printedBanner = printBanner(environment);

这一步就是加载打印banner的核心。

代码语言:javascript复制
private Banner printBanner(ConfigurableEnvironment environment) {
//如果bannerMode是关闭的,就会不打印banner
	if (this.bannerMode == Banner.Mode.OFF) {
		return null;
	}
	//获取资源加载器,如果当前没有就使用默认的资源加载器
	ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader
			: new DefaultResourceLoader(null);
//创建一个SpringBoot应用程序的banner打印类,如果通过setBanner设置,this.banner会有值
	SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);
	//如果bannerMode 是打印到log文件
	if (this.bannerMode == Mode.LOG) {
		return bannerPrinter.print(environment, this.mainApplicationClass, logger);
	}
	//默认打印到控制台
	return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}

先看下如何打印到log文件:

代码语言:javascript复制
Banner print(Environment environment, Class<?> sourceClass, Log logger) {
//获取Banner对象
	Banner banner = getBanner(environment);
	try {
	//使用log 的info 级别打印
		logger.info(createStringFromBanner(banner, environment, sourceClass));
	}
	catch (UnsupportedEncodingException ex) {
		logger.warn("Failed to create String for banner", ex);
	}
	//创建一个打印banner的装饰器bean,允许后期再次使用
	return new PrintedBanner(banner, sourceClass);
}

如何获取的Banner对象:

代码语言:javascript复制
private Banner getBanner(Environment environment) {
//获取txt文本banner
	Banner textBanner = getTextBanner(environment);
	if (textBanner != null) {
		return textBanner;
	}
	//fallbackBanner 为前期通过setBanner设置的自定义banner
	//可见如果两者同时设置,优先使用的txt文本banner
	if (this.fallbackBanner != null) {
		return this.fallbackBanner;
	}
	//都没有,返回一个默认的SpringBootBanner
	return DEFAULT_BANNER;
}

如果获取txt文本banner:

代码语言:javascript复制
private Banner getTextBanner(Environment environment) {
//从环境变量中获取spring.banner.location指定的banner地址,如果没有,使用banner.txt
	String location = environment.getProperty(BANNER_LOCATION_PROPERTY, DEFAULT_BANNER_LOCATION);
	//获取指定的资源对象
	Resource resource = this.resourceLoader.getResource(location);
	try {
	//资源存在,并且资源路径中不包含liquibase-core
		if (resource.exists() && !resource.getURL().toExternalForm().contains("liquibase-core")) {
		//创建一个从源打印的banner对象,实现了Banner接口
			return new ResourceBanner(resource);
		}
	}
	catch (IOException ex) {
		// Ignore
	}
	return null;
}

获取到文本banner的ResourceBanner资源对象后,回到print(Environment environment, Class<?> sourceClass, Log logger) 这个方法中,接下来就是要把获取到banner对象打印出来,createStringFromBanner将获取到banner对象,调用其中的printBanner方法,把输出流转为UTF-8的字符串输出到log文件中。

代码语言:javascript复制
private String createStringFromBanner(Banner banner, Environment environment, Class<?> mainApplicationClass)
		throws UnsupportedEncodingException {
	ByteArrayOutputStream baos = new ByteArrayOutputStream();
	banner.printBanner(environment, mainApplicationClass, new PrintStream(baos));
	String charset = environment.getProperty("spring.banner.charset", "UTF-8");
	return baos.toString(charset);
}

ResourceBanner的printBanner方法:

代码语言:javascript复制
public void printBanner(Environment environment, Class<?> sourceClass, PrintStream out) {
	try {
	//从流读取banner.txt 字符串,使用spring.banner.charset编码,或者UTF-8
		String banner = StreamUtils.copyToString(this.resource.getInputStream(),
				environment.getProperty("spring.banner.charset", Charset.class, StandardCharsets.UTF_8));
				//获取用于解析占位符的所有属性源
		for (PropertyResolver resolver : getPropertyResolvers(environment, sourceClass)) {
		//解析banner,使用PropertyPlaceholderHelper工具类解析
			banner = resolver.resolvePlaceholders(banner);
		}
		//输出到流中
		out.println(banner);
	}
	catch (Exception ex) {
		logger.warn(LogMessage.format("Banner not printable: %s (%s: '%s')", this.resource, ex.getClass(),
				ex.getMessage()), ex);
	}
}

bannerPrinter.print(environment, this.mainApplicationClass, System.out);

打印到控制台的逻辑也是一样的,只是直接输出到控制台

代码语言:javascript复制
Banner print(Environment environment, Class<?> sourceClass, PrintStream out) {
	Banner banner = getBanner(environment);
	banner.printBanner(environment, sourceClass, out);
	return new PrintedBanner(banner, sourceClass);
}

总结

通过图来总结一下整个流程

作者其他要推荐的文章,欢迎来学习: Prometheus 系列文章

  1. Prometheus 的介绍和安装
  2. 直观感受PromQL及其数据类型
  3. PromQL之选择器和运算符
  4. PromQL之函数
  5. Prometheus 告警机制介绍及命令解读
  6. Prometheus 告警模块配置深度解析
  7. Prometheus 配置身份认证
  8. Prometheus 动态拉取监控服务
  9. Prometheus 监控云Mysql和自建Mysql

Grafana 系列文章,版本:OOS v9.3.1

  1. Grafana 的介绍和安装
  2. Grafana监控大屏配置参数介绍(一)
  3. Grafana监控大屏配置参数介绍(二)
  4. Grafana监控大屏可视化图表
  5. Grafana 查询数据和转换数据
  6. Grafana 告警模块介绍
  7. Grafana 告警接入飞书通知

Spring Boot Admin 系列

  1. Spring Boot Admin 参考指南
  2. SpringBoot Admin服务离线、不显示健康信息的问题
  3. Spring Boot Admin2 @EnableAdminServer的加载
  4. Spring Boot Admin2 AdminServerAutoConfiguration详解
  5. Spring Boot Admin2 实例状态监控详解
  6. Spring Boot Admin2 自定义JVM监控通知
  7. Spring Boot Admin2 自定义异常监控
  8. Spring Boot Admin 监控指标接入Grafana可视化

0 人点赞