springboot配置启动自定义banner及配置原理分析
前言
我们注意到springboot项目启动时,控制台会打印自带的banner,然后对于部分比较骚气的同学来说,太单调太普通太一般了;所以,是时候表演真正的技术了
自定义banner
很简单,只需在resources目录下添加自定义的banner.txt文件即可
banner.txt内容
${AnsiColor.BRIGHT_RED} //////////////////////////////////////////////////////////////////// // _ooOoo_ // // o8888888o // // 88" . "88 // // (| ^_^ |) // // O\ = /O // // ____/`---'\____ // // .' \\| |// `. // // / \\||| : |||// \ // // / _||||| -:- |||||- \ // // | | \\\ - /// | | // // | \_| ''\---/'' | | // // \ .-\__ `-` ___/-. / // // ___`. .' /--.--\ `. . ___ // // ."" '< `.___\_<|>_/___.' >'"". // // | | : `- \`.;`\ _ /`;.`/ - ` : | | // // \ \ `-. \_ __\ /__ _/ .-` / / // // ========`-.____`-.___\_____/___.-`____.-'======== // // `=---=' // // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // // 南无阿弥陀佛 南无阿弥陀佛 南无阿弥陀佛 // ////////////////////////////////////////////////////////////////////
=================分隔线 ================================
作为一名骚气的程序猿,我们当然不能只停留于会配置自定义banner,下面咱们去源码里瞅瞅为什么这样配置就能实现自定义banner,走起~
首先进入Application的main函数启动入口的run方法,下面我们一步步跟进去瞧瞧
SpringApplication这个类 public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) { return run(new Class<?>[] { primarySource }, args); }
//new SpringApplication(primarySources)这个咱们不用看,里面是springboot自动装配(SPI)的逻辑,咱们直接看后面的run(args) public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); } 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 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; }
banner处理逻辑
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); }
//banner没有关闭且没有指定是写到log文件中的时候,会走到这一步 return bannerPrinter.print(environment, this.mainApplicationClass, System.out); } public Banner print(Environment environment, Class<?> sourceClass, PrintStream out) {
//这里获取banner内容 Banner banner = getBanner(environment); banner.printBanner(environment, sourceClass, out); return new PrintedBanner(banner, sourceClass); } private Banner getBanner(Environment environment) { Banners banners = new Banners();
//我们没有配置最骚气的图片类型的banner内容(做人要含蓄一点),所以我们看下一步 banners.addIfNotNull(getImageBanner(environment));
//我们配置的是文本型的banner,所以看这里的getTextBanner(environment) banners.addIfNotNull(getTextBanner(environment)); if (banners.hasAtLeastOneBanner()) { return banners; } if (this.fallbackBanner != null) { return this.fallbackBanner; } return DEFAULT_BANNER; }
//到这里同学应该就知道原理了 private Banner getTextBanner(Environment environment) {
//拿到自定义配置的banner文件地址 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; }
//DEFAULT_BANNER_LOCATION常量就指向了banner.txt文件 static final String DEFAULT_BANNER_LOCATION = "banner.txt";