在上一篇文章中总结了4种修改banner打印的方法,那么banner打印底层原理是怎么样的呢??
## 在run方法中
public ConfigurableApplicationContext run(String... args) {
《!---**banner打印的核心方法** ----!》
Banner printedBanner = printBanner(environment);
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:我们进入到Banner printedBanner = printBanner(environment);
private Banner printBanner(ConfigurableEnvironment environment) {
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);
if (this.bannerMode == Mode.LOG) {
return bannerPrinter.print(environment, this.mainApplicationClass, logger);
}
return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}
2:我们进入到bannerPrinter.print(environment, this.mainApplicationClass, System.out);
public Banner print(Environment environment, Class<?> sourceClass, PrintStream out) {
Banner banner = getBanner(environment);
banner.printBanner(environment, sourceClass, out);
return new PrintedBanner(banner, sourceClass);
}
3:看下如何获取banner实例
private Banner getBanner(Environment environment) {
Banners banners = new Banners();
banners.addIfNotNull(getImageBanner(environment));
banners.addIfNotNull(getTextBanner(environment));
if (banners.hasAtLeastOneBanner()) {
return banners;
}
if (this.fallbackBanner != null) {
return this.fallbackBanner;
}
return DEFAULT_BANNER;
}
4:首选看下Banners类的源码
private static class Banners implements Banner {
private final List<Banner> banners = new ArrayList<>();
public void addIfNotNull(Banner banner) {
if (banner != null) {
this.banners.add(banner);
}
}
public boolean hasAtLeastOneBanner() {
return !this.banners.isEmpty();
}
@Override
public void printBanner(Environment environment, Class<?> sourceClass,
PrintStream out) {
for (Banner banner : this.banners) {
banner.printBanner(environment, sourceClass, out);
}
}
}
5:现在回头看下第3步的,首先是获取图片的banner
private Banner getImageBanner(Environment environment) {
// BANNER_IMAGE_LOCATION_PROPERTY = "spring.banner.image.location"
//spirngboot首先去看下配置文件中有没有配置这个属性,如果配置了就会根据
//路径去加载,然后返回一个ImageBanner的实例回去
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
}
//如果没有配置 "spring.banner.image.location" 这个属性,那么就会在resources目录下
//找是否有banner.png || banner.jpg || banner.gif三个文件
//为什么是这三个呢??
//看下 String[] IMAGE_EXTENSION = { "gif", "jpg", "png" }
//如果找到了就会返回一个ImageBanner实例回去
for (String ext : IMAGE_EXTENSION) {
Resource resource = this.resourceLoader.getResource("banner." + ext)
if (resource.exists()) {
return new ImageBanner(resource)
}
}
//如果以上二者都没有,那么直接返回null
return null
}
6:同理,我们看下获取文本的banner
private Banner getTextBanner(Environment environment) {
//先看下这二个属性:
// String BANNER_LOCATION_PROPERTY = "spring.banner.location"
// String DEFAULT_BANNER_LOCATION = "banner.txt"
// SpringBoot会首先去配置文件找,看是否配置了 "spring.banner.location" 这个属性,配置了就会
//去加载这个文本内容,如果没有配置就会在reources目录下去找banner.txt文件,从这个可以看出
//配置文件的优先级是高于banner.txt的,找到其中的一个资源后就会返回一个ResourceBanner实例
String location = environment.getProperty(BANNER_LOCATION_PROPERTY,
DEFAULT_BANNER_LOCATION)
Resource resource = this.resourceLoader.getResource(location)
if (resource.exists()) {
return new ResourceBanner(resource)
}
//如果以上二个都没找到就直接返回null
return null
}
7: 再看下获取banner的方法,如果文本和图片的banner都为空,就会返回默认的banner,现在看下默认的banner是怎么样的
private Banner getBanner(Environment environment) {
Banners banners = new Banners();
banners.addIfNotNull(getImageBanner(environment));
banners.addIfNotNull(getTextBanner(environment));
if (banners.hasAtLeastOneBanner()) {
return banners;
}
if (this.fallbackBanner != null) {
return this.fallbackBanner;
}
return DEFAULT_BANNER;
}
8:查看默认banner,我们点击DEFAULT_BANNER
private static final Banner DEFAULT_BANNER = new SpringBootBanner();
默认的banner实现是SpringBootBanner(),看下这个类,我只截取前面一部分,是不是很熟悉,就是我们默认的banner打印的内容
class SpringBootBanner implements Banner {
private static final String[] BANNER = { "",
" . ____ _ __ _ _",
" /\\\\ / ___'_ __ _ _(_)_ __ __ _ \\ \\ \\ \\",
"( ( )\\___ | '_ | '_| | '_ \\/ _` | \\ \\ \\ \\",
" \\\\/ ___)| |_)| | | | | || (_| | ) ) ) )",
" ' |____| .__|_| |_|_| |_\\__, | / / / /",
" =========|_|==============|___/=/_/_/_/" };
private static final String SPRING_BOOT = " :: Spring Boot :: ";
private static final int STRAP_LINE_SIZE = 42;
9:现在所有的banner获取到了,那么看下打印吧,回头看下第2步,从3-8都是解析获取banner实例的
public Banner print(Environment environment, Class<?> sourceClass, PrintStream out) {
Banner banner = getBanner(environment);
banner.printBanner(environment, sourceClass, out);
return new PrintedBanner(banner, sourceClass);
}
10:现在看下打印banner
因为获取的Banner实例是Banners对象,可以从第7步看出返回的Banners对象,所以我们到Banners类的printBanner方法中
@Override
public void printBanner(Environment environment, Class<?> sourceClass,
PrintStream out) {
for (Banner banner : this.banners) {
banner.printBanner(environment, sourceClass, out);
}
}
11:最后就是获取文件内容然后打印到控制台上