Spring Boot源码分析七:printBanner

前言

本文是作者写关于Spring源码的第一篇文章,作者水平有限,所有的源码文章仅限用作个人学习记录。文中如有错误欢迎各位留言指正。

run

java 复制代码
public ConfigurableApplicationContext run(String... args) {
// 为了计时用的,老版本和新版本不一样
   long startTime = System.nanoTime();
   // 初始化一个引导器的上下文,这是属于Spring Boot的上下文。后边还有一个Spring的上下文。apach好喜欢context这个东西,证明写框架这个context是真的好用。
   DefaultBootstrapContext bootstrapContext = createBootstrapContext();
   // 这是Spring的上下文,在这里定义,在下面进行的初始化
   ConfigurableApplicationContext context = null;
   // 配置一个系统属性
   configureHeadlessProperty();
   // 获取配置文件的监听器 重点 也是扩展点,凡是读取配置文件的地方都是扩展点,因为配置在配置文件中的initializer、listener都会在某个阶段被调用
   SpringApplicationRunListeners listeners = getRunListeners(args);
   // 调用监听器发送启动事件,这里可以通过自定义监听器消费这个事件,处理自己的逻辑
   listeners.starting(bootstrapContext, this.mainApplicationClass);
   try {
   // 解析命令行参数 将其封装成一个ApplicationArguments,这个类的变量name被设置成commandLineArgs字符串,变量source是解析args封装的CommandLineArgs对象。
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
      // 环境预处理
      ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
      // 配置忽略beanInfo
      configureIgnoreBeanInfo(environment);
      // 打印banner信息
      Banner printedBanner = printBanner(environment);
      context = createApplicationContext();
      context.setApplicationStartup(this.applicationStartup);
      prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
      refreshContext(context);
      afterRefresh(context, applicationArguments);
      Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
      if (this.logStartupInfo) {
         new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
      }
      listeners.started(context, timeTakenToStartup);
      callRunners(context, applicationArguments);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, listeners);
      throw new IllegalStateException(ex);
   }
   try {
      Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
      listeners.ready(context, timeTakenToReady);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, null);
      throw new IllegalStateException(ex);
   }
   return context;
}

printBanner

java 复制代码
private Banner printBanner(ConfigurableEnvironment environment) {
判断banner的模式,一共有三种模式off、console、log,见下图一。默认是console
   if (this.bannerMode == Banner.Mode.OFF) {
      return null;
   }
   resourceLoader默认是null,构造函数中进行的赋值,函数的对应的参数就是null。
   ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader
         : new DefaultResourceLoader(null);
   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);
}
  • 图一

print

java 复制代码
Banner print(Environment environment, Class<?> sourceClass, Log logger) {
获取banner 这里面可以解密我们自定义banner的原理
   Banner banner = getBanner(environment);
   try {
   打印信息
      logger.info(createStringFromBanner(banner, environment, sourceClass));
   }
   catch (UnsupportedEncodingException ex) {
      logger.warn("Failed to create String for banner", ex);
   }
   return new PrintedBanner(banner, sourceClass);
}

getBanner

java 复制代码
private Banner getBanner(Environment environment) {
   Banners banners = new Banners();
   添加图片的banner
   banners.addIfNotNull(getImageBanner(environment));
   添加文本的banner
   banners.addIfNotNull(getTextBanner(environment));
   是否至少包含一个banner
   if (banners.hasAtLeastOneBanner()) {
   如果是直接返回banners
      return banners;
   }
   参数其实是null
   if (this.fallbackBanner != null) {
      return this.fallbackBanner;
   }
   默认banner 见下面的图一
   return DEFAULT_BANNER;
}

是不是很熟悉。

getImageBanner

  • 如果配置了spring.banner.image.location属性 直接读取这个属性的资源作为imagebanner
  • 如果没有配置上面的属性,直接在classpath下放置一个banner命名,"gif", "jpg", "png" 这些后缀的图片格式也可以被加载称为imagebanner
java 复制代码
private Banner getImageBanner(Environment environment) {
获取spring.banner.image.location属性值,是从环境变量中获取的,那我们可以在环境变量中设置这个属性哟
   String location = environment.getProperty(BANNER_IMAGE_LOCATION_PROPERTY);
  
   if (StringUtils.hasLength(location)) {
   如果有这个属性就用资源加载器加载该资源
      Resource resource = this.resourceLoader.getResource(location);
      资源不为空 直接转成图片类型的banner
      return resource.exists() ? new ImageBanner(resource) : null;
   }
   for (String ext : IMAGE_EXTENSION) {
   可以加载这些后缀的banner图片{ "gif", "jpg", "png" }
      Resource resource = this.resourceLoader.getResource("banner." + ext);
      if (resource.exists()) {
         return new ImageBanner(resource);
      }
   }
   return null;
}

getTextBanner

java 复制代码
private Banner getTextBanner(Environment environment) {
或者spring.banner.location这个环境变量属性值,所以这里我们也是可以配置属性来控制加载哪个banner,如果不存在默认值为banner.txt
   String location = environment.getProperty(BANNER_LOCATION_PROPERTY, DEFAULT_BANNER_LOCATION);
   加载类路径下的spring.banner.location资源或者直接加载banner.txt
   Resource resource = this.resourceLoader.getResource(location);
   try {
      if (resource.exists() && !resource.getURL().toExternalForm().contains("liquibase-core")) {
      存在就生成banner资源
         return new ResourceBanner(resource);
      }
   }
   catch (IOException ex) {
      // Ignore
   }
   return null;
}

createStringFromBanner

java 复制代码
private String createStringFromBanner(Banner banner, Environment environment, Class<?> mainApplicationClass)
      throws UnsupportedEncodingException {
   ByteArrayOutputStream baos = new ByteArrayOutputStream();
   默认的话是SpringBootBanner的printBanner
   banner.printBanner(environment, mainApplicationClass, new PrintStream(baos));
   String charset = environment.getProperty("spring.banner.charset", "UTF-8");
   return baos.toString(charset);
}

printBanner

java 复制代码
public void printBanner(Environment environment, Class<?> sourceClass, PrintStream printStream) {
   for (String line : BANNER) {
   banner信息保存到输入流
      printStream.println(line);
   }
   拼接版本信息
   String version = SpringBootVersion.getVersion();
   version = (version != null) ? " (v" + version + ")" : "";
   StringBuilder padding = new StringBuilder();
   while (padding.length() < STRAP_LINE_SIZE - (version.length() + SPRING_BOOT.length())) {
      padding.append(" ");
   }

版本信息保存到输入流
   printStream.println(AnsiOutput.toString(AnsiColor.GREEN, SPRING_BOOT, AnsiColor.DEFAULT, padding.toString(),
         AnsiStyle.FAINT, version));
换行
   printStream.println();
}
相关推荐
咩咩啃树皮6 小时前
第40篇:Vue3组件化开发精讲——组件拆分、复用、父子通信、工程化架构
java·前端·架构
鱟鲥鳚7 小时前
Spring Boot 集成 LangChain4j:从模型调用到 Tool Calling(Demo版)
java·spring boot
大模型码小白8 小时前
【Python零基础教程】继承、多态与魔法函数:面向对象编程三大核心特性详解
java·大数据·开发语言·人工智能·python·ai编程
腾渊信息科技公司9 小时前
Spring Boot对接MES实战:视觉检测数据自动同步方案
java·人工智能·spring boot·后端·计算机视觉·ai·软件需求
爱笑的源码基地10 小时前
高并发 Redis 缓存门诊HIS系统源码,含财务统计药房进销存
java·程序·门诊系统·诊所系统·云诊所源码
wuqingshun31415910 小时前
TCP超时重传机制是为了解决什么问题?
java
莫逸风13 小时前
【AgentScope 2.0】 0. 学习指南
java·llm·agent·agentscope
隔窗听雨眠13 小时前
Spring Boot在云原生时代的编程范式革新研究
spring boot·后端·云原生
z1234567898613 小时前
2026最新两款AI编程工具深度对比实测
java·数据库·ai编程
yaoxin52112314 小时前
470. Java 反射 - Member 接口与 AccessFlag
java·开发语言·python