下面纯当自己看源码的笔记,虽然添加了新版本的解释,但还是建议直接看视频
文章目录
七、启动配置原理
几个重要的事件回调机制
配置在META-INF/spring.factories
- ApplicationContextInitializer
- SpringApplicationRunListener
只需要放在ioc容器中
- ApplicationRunner
- CommandLineRunner
启动流程:
1、创建SpringApplication对象
视频里面是旧版 springboot ,<mark>这里下面 主要以新版分析</mark>
(流程差不多,新版就改了些名字,有苦说不出。。)
SpringApplication.run
正是 下面springApplication(primarySources)
创建 SpringApplication 对戏那个
<mark>下面,判断是否web 应用</mark>
<mark>下面 看 setInitializers :反射创建 initializers (工厂类)</mark>
下面 先看 classlorder 和 type 的类型
查看 loadFactoryNames 传入的 type 类型 (其实就上面的 ,一直没变)
下面 可以 进入 loadFactoryNames 看了
<mark>把 classLoader 传入 LoadSpringFactorys</mark>
<mark>下面就可以清楚看到:是从META-INF/spring.factories
路径下获取 initializers</mark>
META-INF/spring.factories
<mark>里面有什么?</mark>
一堆的 待加载类 <mark>用了懒加载,即便文件里有很多类,但不会全部加载</mark>
分析下面代码,可以知道,如何实现的 懒加载
<mark>返回:回到前面</mark>
这就把 names 获取到了
<mark>接着,根据上面得到的names
创建 实例</mark>
<mark>配置 listeners</mark>
里面的原理 和 initializers 里面一样都还是在
spring.factories
里面找
点进 set … 方法里面
<mark>最后,决定哪个是主程序</mark>
从多个配置类中找到有main
方法的主配置类
<mark>看哪个 里面有 main 方法,哪个就是主程序</mark>
/** 旧的 springboot ,分离一个 initialize 方法 专门 init (新版直接 在 新的构造方法 SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) 里面处理 )*/
initialize(sources);
private void initialize(Object[] sources) {
//保存主配置类
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
//判断当前是否一个web应用
this.webEnvironment = deduceWebEnvironment(); // 对应新版的 this.webApplicationType
//从类路径下找到META-INF/spring.factories配置的所有ApplicationContextInitializer;然后保存起来
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
//从类路径下找到ETA-INF/spring.factories配置的所有ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//从多个配置类中找到有main方法的主配置类
this.mainApplicationClass = deduceMainApplicationClass();
}
2、运行run方法
回到这幅图
上面讲了 前面 SpringApplication(primarySources)
创建 SpringApplication 对象的 过程
下面讲 后面的 run(args)
方法
点进 run 方法
一开始 做方法初始化
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
...
debugger 看值 :
然后 看下面 的方法
configureHeadlessProperty();
点进去(截图是新版截图 configrueHeadlessProperty 即 configureHeadlessProperty)
上面 看到,做 awt 处理的。 (略过,知道 awt 就行,继续看下面)
下面 是 getRunListeners
...
//获取SpringApplicationRunListeners;从类路径下META-INF/spring.factories
SpringApplicationRunListeners listeners = getRunListeners(args);
...
点进去
看到 getSpringFactoriesInstances
就知道,是从类路径下 META-INF/spring.factories
获取数据
奇怪的,执行过后(我怀疑是 eclipse
分不清 全局变量 导致的 , 全局也有一个 listeners
)
总之,执行过后,就只有下面这个 listeners
来到 下面 这个 listeners.starting
...
//回调所有的获取SpringApplicationRunListener.starting()方法
listeners.starting();
...
...
try {
//封装命令行参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
...
...
//准备环境
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
//创建环境完成后回调SpringApplicationRunListener.environmentPrepared();表示环境准备完成
...
下面
上面, this.isCustomEnvironment
判断是否为自定义环境
下面,打印 banner 图标
...
Banner printedBanner = printBanner(environment);
...
执行之后的控制台:
接着,是非常关键的异步,创建 spring ioc 容器
...
//创建ApplicationContext;决定创建web的ioc还是普通的ioc
context = createApplicationContext();
...
点进去
进去,可以看到,下面,判断是要创建什么容器(Web 还是 普通 还是 Reactive )
返回到 run 方法,看到,我这个 jar 项目就创建了 web 容器
接着 FailureAnalyzers 是 出现异常后,做异常报告的 (忽略)
...
analyzers = new FailureAnalyzers(context);
....
...
//准备上下文环境;将environment保存到ioc中;而且applyInitializers();
//applyInitializers():回调之前保存的所有的ApplicationContextInitializer的initialize方法
//回调所有的SpringApplicationRunListener的contextPrepared();
//
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
...
applyIntializers 是在 环境(上下文 / context)准备好之后,回调之前第一步创建的 initializers
prepareContext
<mark>运行完成以后回调所有的 SpringApplicationRunListener
的 contextLoaded()
</mark>
下面 refreshContext(context);
刷新容器,说明 环境 真正初始化完成
...
//s刷新容器;ioc容器初始化(如果是web应用还会创建嵌入式的Tomcat);Spring注解版
//扫描,创建,加载所有组件的地方;(配置类,组件,自动配置)
refreshContext(context);
...
refreshContext
完成之后,控制台 打印很多东西
然后看 afterRefresh
...
//从ioc容器中获取所有的ApplicationRunner和CommandLineRunner进行回调
//ApplicationRunner先回调,CommandLineRunner再回调
afterRefresh(context, applicationArguments);
//所有的SpringApplicationRunListener回调finished方法
...
ps 新版中 afterRefresh 被清空了 , 里面就是一个壳
真正做ApplicationRunner
回调 、 和CommandLineRunner
回调 的 是在 新添加的callRunners
方法 里面
然后,下面,是一些 结尾操作。
完成之后,run 方法就基本完成了
···
listeners.finished(context, null);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
//整个SpringBoot应用启动完成以后返回启动的ioc容器;
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}
3、事件监听机制
配置在META-INF/spring.factories
ApplicationContextInitializer
public class HelloApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("ApplicationContextInitializer...initialize..."+applicationContext);
}
}
SpringApplicationRunListener
public class HelloSpringApplicationRunListener implements SpringApplicationRunListener {
//必须有的构造器
public HelloSpringApplicationRunListener(SpringApplication application, String[] args){
}
@Override
public void starting() {
System.out.println("SpringApplicationRunListener...starting...");
}
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
Object o = environment.getSystemProperties().get("os.name"); // 获得系统名
System.out.println("SpringApplicationRunListener...environmentPrepared.."+o);
}
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
System.out.println("SpringApplicationRunListener...contextPrepared...");
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
System.out.println("SpringApplicationRunListener...contextLoaded...");
}
@Override
public void finished(ConfigurableApplicationContext context, Throwable exception) {
System.out.println("SpringApplicationRunListener...finished...");
}
}
配置(META-INF/spring.factories)
org.springframework.context.ApplicationContextInitializer=\
com.atguigu.springboot.listener.HelloApplicationContextInitializer
org.springframework.boot.SpringApplicationRunListener=\
com.atguigu.springboot.listener.HelloSpringApplicationRunListener
只需要放在ioc容器中
ApplicationRunner
@Component
public class HelloApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("ApplicationRunner...run....");
}
}
CommandLineRunner
@Component
public class HelloCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("CommandLineRunner...run..."+ Arrays.asList(args));
}
}
八、自定义starter
starter:
1、这个场景需要使用到的依赖是什么?
2、如何编写自动配置
@Configuration //指定这个类是一个配置类
@ConditionalOnXXX //在指定条件成立的情况下自动配置类生效
@AutoConfigureAfter //指定自动配置类的顺序
@Bean //给容器中添加组件
@ConfigurationPropertie //结合相关xxxProperties类来绑定相关的配置
@EnableConfigurationProperties //让xxxProperties生效加入到容器中
自动配置类要能加载
将需要启动就加载的自动配置类,配置在META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
3、模式:
启动器只用来做依赖导入;
专门来写一个自动配置模块;
启动器依赖自动配置;别人只需要引入启动器(starter)
- mybatis-spring-boot-starter;自定义启动器名-spring-boot-starter
步骤:
1)、启动器模块
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.atguigu.starter</groupId>
<artifactId>atguigu-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
<!--启动器-->
<dependencies>
<!--引入自动配置模块-->
<dependency>
<groupId>com.atguigu.starter</groupId>
<artifactId>atguigu-spring-boot-starter-autoconfigurer</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
2)、自动配置模块
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.atguigu.starter</groupId>
<artifactId>atguigu-spring-boot-starter-autoconfigurer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>atguigu-spring-boot-starter-autoconfigurer</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.10.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--引入spring-boot-starter;所有starter的基本配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
</project>
package com.atguigu.starter;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "atguigu.hello")
public class HelloProperties {
private String prefix;
private String suffix;
public String getPrefix() {
return prefix;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public String getSuffix() {
return suffix;
}
public void setSuffix(String suffix) {
this.suffix = suffix;
}
}
package com.atguigu.starter;
public class HelloService {
HelloProperties helloProperties;
public HelloProperties getHelloProperties() {
return helloProperties;
}
public void setHelloProperties(HelloProperties helloProperties) {
this.helloProperties = helloProperties;
}
public String sayHellAtguigu(String name){
return helloProperties.getPrefix()+"-" +name + helloProperties.getSuffix();
}
}
package com.atguigu.starter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConditionalOnWebApplication //web应用才生效
@EnableConfigurationProperties(HelloProperties.class)
public class HelloServiceAutoConfiguration {
@Autowired
HelloProperties helloProperties;
@Bean
public HelloService helloService(){
HelloService service = new HelloService();
service.setHelloProperties(helloProperties);
return service;
}
}
两个东西(starter 和 autoconfigurer ) 都install
更多SpringBoot整合示例
https://github.com/spring-projects/spring-boot/tree/master/spring-boot-samples