SpringBoot打印banner
本文通过解读springboot启动源码中,看看banner是怎么执行打印,并了解一下可以怎么配置打印
1. 可以打印什么样的banner
不说了,先上代码:
代码截取了SpringApplication.class中run方法:
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
/////////////////////看这里,就是这里执行了打印banner的 ////////////////////////////////
/////////////////////看这里,就是这里执行了打印banner的 ////////////////////////////////
/////////////////////看这里,就是这里执行了打印banner的 ////////////////////////////////
Banner printedBanner = printBanner(environment);
/////////////////////看这里,就是这里执行了打印banner的 ////////////////////////////////
/////////////////////看这里,就是这里执行了打印banner的 ////////////////////////////////
/////////////////////看这里,就是这里执行了打印banner的 ////////////////////////////////
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
1.1. 源码分析可得知:
- 源码中可以看出,打印banner是在springApplication初始化完成以后再执行的(在run方法内执行)。
- 执行打印之前,先加载配置环境ConfigurableEnvironment environment ,实体类是StandardServletEnvironment。
- 打印banner之前,开启了SpringApplicationRunListeners的监听,开始了监听spring的整个生命周期。
- 加载和打印banner,是计算再启动时间内的。
2. 看看printBanner(environment)方法内部
private Banner printBanner(ConfigurableEnvironment environment) {
// private Banner.Mode bannerMode = Banner.Mode.CONSOLE; 默认值是这个
if (this.bannerMode == Banner.Mode.OFF) {
return null;
}
// ResourceLoader这是一个加载资源的接口,下面要加载banner的内容需要的
// private ResourceLoader resourceLoader; 默认值是null
// 所以来ResourceLoader 的实体类是 DefaultResourceLoader
ResourceLoader resourceLoader = (this.resourceLoader != null ? this.resourceLoader
: new DefaultResourceLoader(getClassLoader()));
// 实例化一个打印banner的对象
SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(
resourceLoader, this.banner);
// private Banner.Mode bannerMode = Banner.Mode.CONSOLE; 这是默认值
if (this.bannerMode == Mode.LOG) {
return bannerPrinter.print(environment, this.mainApplicationClass, logger);
}
// 默认情况下执行这里的banner打印
// 在这里,内部执行了读取banner内容和打印banner,所以详细读这个方法内部的源码
return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}
3. SpringApplicationBannerPrinter 源码解读
class SpringApplicationBannerPrinter {
// 打印bananer调用了这个方法
public Banner print(Environment environment, Class<?> sourceClass, PrintStream out) {
// 获取banner对象,返回的Banner对象可能有两种情况
// 1.如果我们配置了banner,就会返回Banners的实体
// 2.如果我们没有配置,就会是spring默认的SpringBootBanner对象
// banner的加入顺序是先添加图片banner在添加文本banner
Banner banner = getBanner(environment);
// 具体的打印代码如果感兴趣,可以去读一读。
// 这里只是简单的提一下,打印的顺序与添加的顺序一致
banner.printBanner(environment, sourceClass, out);
return new PrintedBanner(banner, sourceClass);
}
// 获取banner对象的方法
private Banner getBanner(Environment environment) {
// 这是一个多个banner组成的banner对象
Banners banners = new Banners();
// 添加图片banner
banners.addIfNotNull(getImageBanner(environment));
// 添加文本banner
banners.addIfNotNull(getTextBanner(environment));
// 如果banners不是空的(即,如果我们配置了banner),则返回banners
if (banners.hasAtLeastOneBanner()) {
return banners;
}
// private final Banner fallbackBanner; 默认是null
// 所以默认不会是这个banner
if (this.fallbackBanner != null) {
return this.fallbackBanner;
}
// 这是默认的banner,当我们没有做任何banner配置的时候,就会使用这个默认的springboot的banner对象
/*
. ____ _ __ _ _
/\\\\ / ___'_ __ _ _(_)_ __ __ _ \\ \\ \\ \\
( ( )\\___ | '_ | '_| | '_ \\/ _` | \\ \\ \\ \\
\\\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\\__, | / / / /
=========|_|==============|___/=/_/_/_/
*/
// 这个springboot的banner对象已经很熟悉了,一个大大的springboot,这里就不详细解说了
// private static final Banner DEFAULT_BANNER = new SpringBootBanner();
return DEFAULT_BANNER;
}
// 获取文本banner的方法
private Banner getTextBanner(Environment environment) {
/*
此方法代码并不复杂,可以看出,spring是读取文本banner是两个地方
1.配置文件中spring.banner.location配置的banner
2.如果么有配置spring.banner.location,那么就默认是classPath下的banner.txt文件
*/
// static final String BANNER_LOCATION_PROPERTY = "spring.banner.location";
// static final String DEFAULT_BANNER_LOCATION = "banner.txt";
String location = environment.getProperty(BANNER_LOCATION_PROPERTY, DEFAULT_BANNER_LOCATION);
// 3.读取资源文件
Resource resource = this.resourceLoader.getResource(location);
if (resource.exists()) {
return new ResourceBanner(resource);
}
// 如果读取不到,则认为,我们没有配置文本banner
return null;
}
// 获取图片banner的办法
private Banner getImageBanner(Environment environment) {
// 1.读取配置中的图片banner配置
// static final String BANNER_IMAGE_LOCATION_PROPERTY = "spring.banner.image.location";
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);
}
// 2.读取classPath下的banner.gif、banner.jpg、banner.png
// static final String[] IMAGE_EXTENSION = { "gif", "jpg", "png" };
for (String ext : IMAGE_EXTENSION) {
Resource resource = this.resourceLoader.getResource("banner." + ext);
if (resource.exists()) {
return new ImageBanner(resource);
}
}
// 3.如果都都不到,则认为我们没有配置图片banner
return null;
}
}
4. 总结
通过阅读源码可以得知:
- 配置banner可以有两种,图片banner和文本banner
- 文本banner只能配置一个,图片banner也只能配置一个,如果一个banner都不配置,就会使用默认的springboot的banner。
- 文本banner和图片banner可以同时配置,两个都会打印,先打印图片banner。
- 配置文本banner有两种方式:
- 配置文本banner的路径,配置的key是spring.banner.location。
- 可以不配置spring.banner.location的值,直接在classPath中放入一个banner.txt文件。
- 如果配置了spring.banner.location的值,就不会读取banner.txt文件作为文本banner。
- 配置图片banner也是两种方式:
- 配置图片banner的路径,key是spring.banner.image.location。
- 也可以不配置路径,可以在classPath中添加banner.gif、banner.jpg、banner.png其中一个文件,如果添加多个,只会打印一个,有限级:“gif” > “jpg” > “png”。
- 如果配置了spring.banner.image.location的值,就不会读取banner.gif、banner.jpg、banner.png任何一个作为图片banner。