本节主要介绍SpringBoot Application类相关源码的深入学习。

主要包括:

  1. SpringBoot应用自定义启动配置
  2. SpringBoot应用生命周期,以及在生命周期各个阶段自定义配置。

本节采用SpringBoot 2.1.10.RELASE,对应示例源码在:https://github.com/laolunsi/spring-boot-examples


SpringBoot应用启动过程:

SpringApplication application = new SpringApplication(DemoApplication.class);
application.run(args);

一、Application类自定义启动配置

创建SpringApplication对象后,在调用run方法之前,我们可以使用SpringApplication对象来添加一些配置,比如禁用banner、设置应用类型、设置配置文件(profile)

举例:

@SpringBootApplication
public class DemoApplication { public static void main(String[] args) {
SpringApplication application = new SpringApplication(DemoApplication.class);
// 设置banner禁用
application.setBannerMode(Banner.Mode.OFF);
// 将application-test文件启用为profile
application.setAdditionalProfiles("test");
// 设置应用类型为NONE,即启动完成后自动关闭
application.setWebApplicationType(WebApplicationType.NONE);
application.run(args);
} }

​ 也可以使用SpringApplicationBuilder类来创建SpringApplication对象,builder类提供了链式调用的API,更方便调用,增强了可读性。

        new SpringApplicationBuilder(YqManageCenterApplication.class)
.bannerMode(Banner.Mode.OFF)
.profiles("test")
.web(WebApplicationType.NONE)
.run(args);

二、application生命周期

SpringApplication的生命周期主要包括:

  1. 准备阶段:主要包括加载配置、设置主bean源、推断应用类型(三种)、创建和设置SpringBootInitializer、创建和设置Application监听器、推断主入口类
  2. 运行阶段:开启时间监听、加载运行监听器、创建Environment、打印banner、创建和装载context、广播应用已启动、广播应用运行中

我们先来看一下源码的分析:

SpringBootApplication构造器:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {

        // 设置默认配置
this.sources = new LinkedHashSet();
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.addConversionService = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = new HashSet();
this.isCustomEnvironment = false;
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
// 设置主bean源
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
// 推断和设置应用类型(三种)
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 创建和设置SpringBootInitializer
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 创建和设置SpringBoot监听器
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
// 推断和设置主入口类
this.mainApplicationClass = this.deduceMainApplicationClass();
}

SpringApplication.run方法源码:

public ConfigurableApplicationContext run(String... args) {
// 开启时间监听
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
this.configureHeadlessProperty(); // 加载Spring应用运行监听器(SpringApplicationRunListenter)
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting(); Collection exceptionReporters;
try {
// 创建environment(包括PropertySources和Profiles)
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment); // 打印banner
Banner printedBanner = this.printBanner(environment); // 创建context(不同的应用类型对应不同的上下文)
context = this.createApplicationContext();
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
// 装载context(其中还初始化了IOC容器)
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 调用applicationContext.refresh
this.refreshContext(context);
// 空方法
this.afterRefresh(context, applicationArguments);
stopWatch.stop(); // 关闭时间监听;这样可以计算出完整的启动时间
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
} // 广播SpringBoot应用已启动,会调用所有SpringBootApplicationRunListener里的started方法
listeners.started(context); // 遍历所有ApplicationRunner和CommadnLineRunner的实现类,执行其run方法
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
} try {
// 广播SpringBoot应用运行中,会调用所有SpringBootApplicationRunListener里的running方法
listeners.running(context);
return context;
} catch (Throwable var9) {
// run出现异常时,处理异常;会调用报错的listener里的failed方法,广播应用启动失败,将异常扩散出去
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}

三、application生命周期自定义配置

在SpringApplication的生命周期中,我们还可以添加一些自定义的配置。

下面的配置,主要是通过实现Spring提供的接口,然后在resources下新建META-INF/spring.factories文件,在里面添加这个类而实现引入的。

准备阶段,可以添加如下自定义配置:

3.1 自定义ApplicationContextInitializer的实现类

@Order(100)
public class MyInitializer implements ApplicationContextInitializer { @Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
System.out.println("自定义的应用上下文初始化器:" + configurableApplicationContext.toString());
}
}

再定义一个My2Initializer,设置@Order(101)

然后在spring.factories文件里如下配置:

# initializers
org.springframework.context.ApplicationContextInitializer=\
com.example.applicationdemo.MyInitializer,\
com.example.applicationdemo.My2Initializer

启动项目:


3.2 自定义ApplicationListener的实现类

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
void onApplicationEvent(E var1);
}![file](https://img2018.cnblogs.com/blog/1860493/201911/1860493-20191125130012982-1676057906.png)

即监听ApplicationEvents类的ApplicationListener接口的实现类。

首先查看有多少种ApplicationEvents:

里面还可以进行拆分。

我们这里设置两个ApplicationListener,都用于监听ApplicationEnvironmentPreparedEvent

@Order(200)
public class MyApplicationListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent> { @Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent applicationEnvironmentPreparedEvent) {
System.out.println("MyApplicationListener: 应用环境准备完毕" + applicationEnvironmentPreparedEvent.toString());
}
}

在spring.factories中加入applicationListener的配置:

# application-listeners
org.springframework.context.ApplicationListener=\
com.example.applicationdemo.MyApplicationListener,\
com.example.applicationdemo.MyApplicationListener2

启动阶段,可以添加如下自定义配置:

3.3 自定义SpringBootRunListener的实现类

监听整个SpringBoot应用生命周期

public interface SpringApplicationRunListener {
// 应用启动
void starting(); // 应用ConfigurableEnvironment准备完毕,此刻可以将其调整
void environmentPrepared(ConfigurableEnvironment environment); // 上下文准备完毕
void contextPrepared(ConfigurableApplicationContext context); // 上下文装载完毕
void contextLoaded(ConfigurableApplicationContext context); // 启动完成(Beans已经加载到容器中)
void started(ConfigurableApplicationContext context); // 应用运行中
void running(ConfigurableApplicationContext context); // 应用运行失败
void failed(ConfigurableApplicationContext context, Throwable exception);
}

我们可以自定义SpringApplicationRunListener的实现类,通过重写以上方法来定义自己的listener。

比如:

public class MyRunListener implements SpringApplicationRunListener {

    // 注意要加上这个构造器,两个参数都不能少,否则启动会报错,报错的详情可以看这个类的最下面
public MyRunListener(SpringApplication springApplication, String[] args) { } @Override
public void starting() {
System.out.println("MyRunListener: 程序开始启动");
} // 其他方法省略,不做修改
}

然后在spring.factories文件中添加这个类:

org.springframework.boot.SpringApplicationRunListener=\
com.example.applicationdemo.MyRunListener

启动:


3.4 自定义ApplicationRunner或CommandLineRunner

application的run方法中,有这样一行:

this.callRunners(context, applicationArguments);

仔细分析源码,发现这一句的作用是:SpringBoot应用启动过程中,会遍历所有的ApplicationRunner和CommandLineRunner,执行其run方法。

private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList();
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
AnnotationAwareOrderComparator.sort(runners);
Iterator var4 = (new LinkedHashSet(runners)).iterator(); while(var4.hasNext()) {
Object runner = var4.next();
if (runner instanceof ApplicationRunner) {
this.callRunner((ApplicationRunner)runner, args);
} if (runner instanceof CommandLineRunner) {
this.callRunner((CommandLineRunner)runner, args);
}
} }
@FunctionalInterface
public interface CommandLineRunner {
void run(String... args) throws Exception;
}
@FunctionalInterface
public interface ApplicationRunner {
void run(ApplicationArguments args) throws Exception;
}

分别定义一个实现类,添加@Component,这两个实现类不需要在spring.factories中配置

好了,关于这些自定义配置的具体使用,后续会继续进行介绍,请持续关注!感谢!

具体示例代码请去https://github.com/laolunsi/spring-boot-examples查看。

SpringBoot Application深入学习的更多相关文章

  1. SpringBoot源码学习系列之异常处理自动配置

    SpringBoot源码学习系列之异常处理自动配置 1.源码学习 先给个SpringBoot中的异常例子,假如访问一个错误链接,让其返回404页面 在浏览器访问: 而在其它的客户端软件,比如postm ...

  2. SpringBoot源码学习系列之嵌入式Servlet容器

    目录 1.博客前言简单介绍 2.定制servlet容器 3.变换servlet容器 4.servlet容器启动原理 SpringBoot源码学习系列之嵌入式Servlet容器启动原理 @ 1.博客前言 ...

  3. SpringBoot 企业级核心技术学习专题

    专题 专题名称 专题描述 001 Spring Boot 核心技术 讲解SpringBoot一些企业级层面的核心组件 002 Spring Boot 核心技术章节源码 Spring Boot 核心技术 ...

  4. SpringBoot + Spring Security 学习笔记(五)实现短信验证码+登录功能

    在 Spring Security 中基于表单的认证模式,默认就是密码帐号登录认证,那么对于短信验证码+登录的方式,Spring Security 没有现成的接口可以使用,所以需要自己的封装一个类似的 ...

  5. SpringBoot + Spring Security 学习笔记(三)实现图片验证码认证

    整体实现逻辑 前端在登录页面时,自动从后台获取最新的验证码图片 服务器接收获取生成验证码请求,生成验证码和对应的图片,图片响应回前端,验证码保存一份到服务器的 session 中 前端用户登录时携带当 ...

  6. Springboot Application 集成 OSGI 框架开发

    内容来源:https://www.ibm.com/developerworks/cn/java/j-springboot-application-integrated-osgi-framework-d ...

  7. SpringBoot application.properties (application.yml)优先级从高到低

    SpringBoot application.properties(application.yml) 优先级从高到低 SpringBoot配置文件优先级从高到低 =================== ...

  8. springboot application.properties配置大全

    springboot application.properties配置大全 官方文档 https://docs.spring.io/spring-boot/docs/current/reference ...

  9. springboot日志框架学习------slf4j和log4j2

    springboot日志框架学习------slf4j和log4j2 日志框架的作用,日志框架就是用来记录系统的一些行为的,可以通过日志发现一些问题,在出现问题之后日志是好的一个帮手. 市面上的日志框 ...

随机推荐

  1. 2019.10.24 CSP%你赛第二场d1t3

    题目描述 Description 精灵心目中亘古永恒的能量核心崩溃的那一刻,Bzeroth 大陆的每个精灵都明白,他们的家园已经到了最后的时刻.就在这危难关头,诸神天降神谕,传下最终兵器——潘少拉魔盒 ...

  2. Spring Boot项目如何同时支持HTTP和HTTPS协议

    如今,企业级应用程序的常见场景是同时支持HTTP和HTTPS两种协议,这篇文章考虑如何让Spring Boot应用程序同时支持HTTP和HTTPS两种协议. 准备 为了使用HTTPS连接器,需要生成一 ...

  3. LeetCode 84--柱状图中最大的矩形( Largest Rectangle in Histogram) 85--最大矩形(Maximal Rectangle)

    84题和85五题 基本是一样的,先说84题 84--柱状图中最大的矩形( Largest Rectangle in Histogram) 思路很简单,通过循环,分别判断第 i 个柱子能够延展的长度le ...

  4. Linux 修改网卡名

    1. 修改网卡配置文件 vim /etc/sysconfig/network-scripts/ifcfg-ens32 (“ens32”为当前网卡名) 将NAME.DEVICE项修改为eth0 2.  ...

  5. 实用脚本awk

    非常实用的awk 有时候需要去服务器下载几个日志 日志太多,翻滚起来很麻烦,操作又慢又复杂. 可以使用这个下载最新的两个文件 ls -lt | head -3 | awk -F ' ' '{if(NR ...

  6. OptimalSolution(6)--栈和队列

    一.设计一个有getMin功能的栈 题目:实现一个特殊的栈,在实现栈的基本功能的基础上,再实现返回栈中最小元素的操作.pop.push.getMin操作的时间复杂度都是O(1). 思路:设计两个栈,一 ...

  7. 从《国产凌凌漆》看到《头号玩家》,你就能全面了解5G

    2019 年 9 月,移动.联通.电信5G套餐预约总和已突破 1000 万.2019 年 11 月,三大电信运营商将在全国范围内提供携号转网服务.2019 年内,移动将建立 5 万个 5G 基站,联通 ...

  8. wfi破解

    破解wifi步骤 1.准备字典(常见字典 数字组合.常用姓氏.汉字姓名+年份组合等等) 2.无线网卡 3.查看附近WiFi信息 前言 : 随着无线网络走进我们的生活,在方便了我们的同时又产生了许多的安 ...

  9. [2018-06-27] virtualenv

    在开发Python应用程序的时候,系统安装的Python只有一个版本:3.4.所有第三方的包都会被pip安装到Python3的site-packages目录下. 如果我们要同时开发多个应用程序,那这些 ...

  10. Java零基础入门之常用工具

    Java异常 什么是异常? 在程序运行过程中,意外发生的情况,背离我们程序本身的意图的表现,都可以理解为异常. throwable是所有异常的根类,异常分为两种异常exception和error Er ...