下面纯当自己看源码的笔记,虽然添加了新版本的解释,但还是建议直接看视频

七、启动配置原理

几个重要的事件回调机制

配置在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>运行完成以后回调所有的 SpringApplicationRunListenercontextLoaded()</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