springboot源码分析2-springboot 之banner定制以及原理

简介: 1. springboot源码分析2-springboot 之banner定制以及原理springboot在启动的时候,默认会在控制台输出默认的banner。

1. springboot源码分析2-springboot banner定制以及原理

springboot在启动的时候,默认会在控制台输出默认的banner。也就是我们经常所说的图案,输出的图案如下所示:

  .   ____          _            __ _ _

 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \

( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \

 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )

  '  |____| .__|_| |_|_| |_\__, | / / / /

 =========|_|==============|___/=/_/_/_/

 :: Spring Boot ::             (v2.0.0.M3)

本小节围绕上述的图案也就是banner展开进行说明:包括banner的输出模式、定制、关闭、原理分析。

1.1 banner的三种输出模式

banner的输出默认有三种种模式,LOG、CONSOLE、OFF。

1. LOG:banner信息输出到日志文件。

2. CONSOLE:banner信息输出到控制台。

3. OFF:禁用banner的信息输出。

1.2 banner的输出模式设置

上述的三种输出模式,我们可以在程序中进行设置,示例代码如下:

1 @SpringBootApplication

2 public class DemoApplication {

3  public static void main(String[] args) {

4     SpringApplication springApplication=new SpringApplication();

5      springApplication.setBannerMode(Banner.Mode.CONSOLE);//设置输出模式

6      springApplication.run(DemoApplication.class, args);

7  }

8 }

springApplication.setBannerMode方法用于设置banner输出模式,该方法需要一个Model类型的参数,如下所示:

1 public interface Banner {

2  void printBanner(Environment environment, Class<?> sourceClass, PrintStream out);

3   enum Mode {

4        OFF,

5         CONSOLE,

6         LOG

7    }

8 }

1.3 原理分析

由于springboot中的源码比较复杂,因此我们讲解的时候采取分模块拆解的方式去学习,而不是全盘托出。对于需要讲解的我们要重点关注,对于自己暂时还不熟悉的,我们就留到后面的章节再去单独讲解。这样就可以尽量避免在源码中跟踪来跟踪去,最后不知所云了。

1.3.1 banner类的架构

上述我们看到了banner的三种输出模式,那么问题来了,这些不同的输出模式框架是怎么处理的呢?banner内容如何进行定义呢?banner的定义以及内容有没有限制呢?带着这些问题,我们先看下banner类的顶级接口Bannerorg.springframework.boot.Banner)的核心方法如下所示:

1 public interface Banner {

2     void printBanner(Environment environment, Class<?> sourceClass, PrintStream out);

}

Banner类中只有printBanner(打印Banner)一个方法,该方法顾名思义就是将Banner打印到指定的流中(前面我们也提到过、可以是文件也可以是控制台)。这个接口的实现类如下图所示:

           

1 Banners:该类内部持有一系列的Banner处理类集合,并在打印流的时候,循环遍历所有的Banner处理类集合进行处理。

2 ImageBanner:打印图片形式的Banner

3 PrintedBanner:负责装饰其他的Banner类,并内部进行调用。这个后续再详细讲解,现有一个印象即可。

4 ResourceBanner:从一个文本资源中获取需要打印的Banner并输出。

5 SpringBootBanner:默认的Banner打印类。(默认的)

 

好了,接下来看一下Banner类的初始化以及内部处理逻辑吧。

 首先,我们看一下SpringApplication类中的run(String... args)方法,如下所示:

 

暂时将精力放置到printBanner方法的执行逻辑中,该方法如下所示:

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);

}

上述函数的处理逻辑如下:

1、首先判断banner的输出级别。如果禁用了,则直接返回空。关于日志的输出级别可以参考上文的讲解。

2、获取资源加载器ResourceLoader 。ResourceLoader我们也是可以自定义的,如果你觉得有必要。资源加载器主要加载项目或者jar包中定义好的资源。因为spring将所有的配置文件或者属性文件抽象为资源Resource所以资源加载器的目的就是加载指定的配置文件或者属性文件并将其封装为Resource实例对象。

3、实例化SpringApplicationBannerPrinter 类,注意一点:this.banner我们是可以自定义的,操作方式如下:

SpringApplication springApplication=new SpringApplication();

springApplication.setBannerMode(Banner.Mode.CONSOLE);

Resource resource=new ClassPathResource("banner.txt");

springApplication.setBanner(new ResourceBanner(resource));

上述代码中,我们指定了自己的ResourceBanner并让其加载项目根目录中的banner.txt资源。

注意:spring将项目中的所有配置文件或者属性文件抽象为资源Resource。关于这一点一定要牢记于心。

4、如果banner的输出模式是Mode.LOG,则直接将其信息输出到logger日志中,否则将其输出到控制台,也就是System.out。

logger的定义如下所示:

private static final Log logger = LogFactory.getLog(SpringApplication.class);

通过这个地方的处理逻辑可以看出,框架并没有给我们一个自定义的开关来做这个事情。也是写死的方式处理的。

1.3.2 SpringApplicationBannerPrinter类

下面开始重点讲解第三个步骤,也就是SpringApplicationBannerPrinter类的实例化操作。该类的构造函数定义如下所示:

1 private final ResourceLoader resourceLoader;

2  private final Banner fallbackBanner;

3 SpringApplicationBannerPrinter(ResourceLoader resourceLoader, Banner fallbackBanner) {

4  this.resourceLoader = resourceLoader;

5  this.fallbackBanner = fallbackBanner;

6  }

在这里,我们暂时记住SpringApplicationBannerPrinter类中持有resourceLoader和fallbackBanner即可。

接下来,我们看一下bannerPrinter.print(environment, this.mainApplicationClass, System.out)这行代码所做的事情,该方法的定义如下所示:

1 public Banner print(Environment environment, Class<?> sourceClass, PrintStream out) {

2     Banner banner = getBanner(environment);//获取Banner

3     banner.printBanner(environment, sourceClass, out);//打印

4     return new PrintedBanner(banner, sourceClass);//实例化PrintedBanner类。

5  }

亦然是三部曲:1、获取Banner;2、调用Banner中的printBanner方法;3、实例化PrintedBanner类。

1.3.2.1. 获取Banner

getBanner(environment)方法的定义如下所示:

1 private Banner getBanner(Environment environment) {

2  Banners banners = new Banners();

3  banners.addIfNotNull(getImageBanner(environment));

4  banners.addIfNotNull(getTextBanner(environment));

5  if (banners.hasAtLeastOneBanner()) {

6  return banners;

7  }

8  if (this.fallbackBanner != null) {

9  return this.fallbackBanner;

10  }

11  return DEFAULT_BANNER;

12  }

我们将其执行逻辑进行如下的总结。

1、实例化Banners类,该类内部持有一系列的Banner实例集合。我们可以将其理解为容器。

2、获取ImageBanner、获取TextBanner。

3、如果Banners类中已经包含至少一个Banner实例了,则直接返回,防止Banner太多了。

4、如果Banners类中没有任何Banner实例,则判断fallbackBanner是否为空,如果不为空,则直接返回。注意:这个Banner我们可以自定义,前面已经讲解了,相信大家还有一定的印象。

5、如果fallbackBanner为空,则表示系统内置的一系列Banner没找到,fallbackBanner用户也没有定义,那就没办法了,直接返回默认的Banner也就是SpringBootBanner

1.3.2.2. 获取ImageBanner

接下来一个个的看吧。首先看一下getImageBanner的逻辑。该方法的实例代码如下所示:

1  static final String BANNER_LOCATION_PROPERTY = "banner.location";

2  static final String BANNER_IMAGE_LOCATION_PROPERTY = "banner.image.location";

3  static final String DEFAULT_BANNER_LOCATION = "banner.txt";

4  static final String[] IMAGE_EXTENSION = { "gif", "jpg", "png" }

5  private Banner getImageBanner(Environment environment) {

6    String location = environment.getProperty(BANNER_IMAGE_LOCATION_PROPERTY);

7    if (StringUtils.hasLength(location)) {

8      Resource resource = this.resourceLoader.getResource(location);

9      return (resource.exists() ? new ImageBanner(resource) : null);

10    }

11    for (String ext : IMAGE_EXTENSION) {

12      Resource resource = this.resourceLoader.getResource("banner." + ext);

13      if (resource.exists()) {

14        return new ImageBanner(resource);

15      }

16    }

17    return null;

18  }

1、首先,通过environment获取banner.image.location变量的值,在这里如果对于environment不太了解的小伙伴不要担心,这个后续会详细的讲解,我们在这里只需要知道可以通过environment获取到所有的属性信息即可,比如可以通过environment获取到application.properties文件中所有的属性以及值。

2、如果banner.image.location变量存在并且路径是正确的,则直接实例化ImageBanner类并返回,否则开始获取banner.gif、banner.jpg  banner.png。三者只要任意一个存在则直接实例化ImageBanner类并返回。

 

1.3.2.3.  获取TextBanner

getTextBanner(environment)方法的代码如下:

1 static final String BANNER_LOCATION_PROPERTY = "banner.location";

2  static final String DEFAULT_BANNER_LOCATION = "banner.txt";

3 private Banner getTextBanner(Environment environment) {

4  String location = environment.getProperty(BANNER_LOCATION_PROPERTY,

5  DEFAULT_BANNER_LOCATION);

6  Resource resource = this.resourceLoader.getResource(location);

7  if (resource.exists()) {

8  return new ResourceBanner(resource);

9  }

10  return null;

11  }

getTextBanner核心逻辑可以将其总结如下:

1、获取banner.location值,如果没有配置,则直接使用内置的banner.txt。看到这里貌似明白了,我们默认的行为就是该方式,如果期望打印banner信息,只需要在项目的根目录中建立一个banner.txt文件,并填写相应的信息即可替换默认的输出内容(上文已经提及到)。

2、如果banner.location值不为空,并在其配置的文件存在,则直接实例化ResourceBanner并返回。

关于banner.txt目录结构的配置如下所示:

 

如果我们需要通过banner.location方式指定,则可以直接在application.properties文件中进行如下的设置:

banner.location=banner.txt。当然了这种方式的话banner.txt文件的名称可以自定义。

 

本文我们暂时先讲解到这里,关于一系列的banner子类,我们下一篇文章一个个的讲解。大家有兴趣的话可以自行跟踪一下。


欢迎关注我的微信公众号,第一时间获得博客更新提醒,以及更多成体系的Java相关原创技术干货。 
扫一扫下方二维码或者长按识别二维码,即可关注。
 

   

作者:分享牛
         
本博客中未标明转载的文章归作者 分享牛所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

相关文章
|
1月前
|
XML Java 开发者
Spring Boot开箱即用可插拔实现过程演练与原理剖析
【11月更文挑战第20天】Spring Boot是一个基于Spring框架的项目,其设计目的是简化Spring应用的初始搭建以及开发过程。Spring Boot通过提供约定优于配置的理念,减少了大量的XML配置和手动设置,使得开发者能够更专注于业务逻辑的实现。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,为开发者提供一个全面的理解。
36 0
|
3天前
|
Java 数据库连接 Maven
最新版 | 深入剖析SpringBoot3源码——分析自动装配原理(面试常考)
自动装配是现在面试中常考的一道面试题。本文基于最新的 SpringBoot 3.3.3 版本的源码来分析自动装配的原理,并在文未说明了SpringBoot2和SpringBoot3的自动装配源码中区别,以及面试回答的拿分核心话术。
最新版 | 深入剖析SpringBoot3源码——分析自动装配原理(面试常考)
|
10天前
|
NoSQL Java Redis
Spring Boot 自动配置机制:从原理到自定义
Spring Boot 的自动配置机制通过 `spring.factories` 文件和 `@EnableAutoConfiguration` 注解,根据类路径中的依赖和条件注解自动配置所需的 Bean,大大简化了开发过程。本文深入探讨了自动配置的原理、条件化配置、自定义自动配置以及实际应用案例,帮助开发者更好地理解和利用这一强大特性。
55 14
|
1月前
|
Java Spring
SpringBoot入门 - 定制自己的Banner
SpringBoot入门 - 定制自己的Banner
26 2
SpringBoot入门 - 定制自己的Banner
|
1月前
|
Java Spring
SpringBoot自动装配的原理
在Spring Boot项目中,启动引导类通常使用`@SpringBootApplication`注解。该注解集成了`@SpringBootConfiguration`、`@ComponentScan`和`@EnableAutoConfiguration`三个注解,分别用于标记配置类、开启组件扫描和启用自动配置。
59 17
|
1月前
|
Java 容器
springboot自动配置原理
启动类@SpringbootApplication注解下,有三个关键注解 (1)@springbootConfiguration:表示启动类是一个自动配置类 (2)@CompontScan:扫描启动类所在包外的组件到容器中 (3)@EnableConfigutarion:最关键的一个注解,他拥有两个子注解,其中@AutoConfigurationpackageu会将启动类所在包下的所有组件到容器中,@Import会导入一个自动配置文件选择器,他会去加载META_INF目录下的spring.factories文件,这个文件中存放很大自动配置类的全类名,这些类会根据元注解的装配条件生效,生效
|
1月前
|
Java Spring
SpringBoot入门(5) - 定制自己的Banner
SpringBoot入门(5) - 定制自己的Banner
13 0
 SpringBoot入门(5) - 定制自己的Banner
|
5月前
|
Java 应用服务中间件 开发者
Java面试题:解释Spring Boot的优势及其自动配置原理
Java面试题:解释Spring Boot的优势及其自动配置原理
130 0
|
2月前
|
Java Spring 容器
springboot @RequiredArgsConstructor @Lazy解决循环依赖的原理
【10月更文挑战第15天】在Spring Boot应用中,循环依赖是一个常见问题,当两个或多个Bean相互依赖时,会导致Spring容器陷入死循环。本文通过比较@RequiredArgsConstructor和@Lazy注解,探讨它们解决循环依赖的原理和优缺点。@RequiredArgsConstructor通过构造函数注入依赖,使代码更简洁;@Lazy则通过延迟Bean的初始化,打破创建顺序依赖。两者各有优势,需根据具体场景选择合适的方法。
121 4
|
3月前
|
Java 应用服务中间件 API
Vertx高并发理论原理以及对比SpringBoot
Vertx 是一个基于 Netty 的响应式工具包,不同于传统框架如 Spring,它的侵入性较小,甚至可在 Spring Boot 中使用。响应式编程(Reactive Programming)基于事件模式,通过事件流触发任务执行,其核心在于事件流 Stream。相比多线程异步,响应式编程能以更少线程完成更多任务,减少内存消耗与上下文切换开销,提高 CPU 利用率。Vertx 适用于高并发系统,如 IM 系统、高性能中间件及需要较少服务器支持大规模 WEB 应用的场景。随着 JDK 21 引入协程,未来 Tomcat 也将优化支持更高并发,降低响应式框架的必要性。
Vertx高并发理论原理以及对比SpringBoot