当前位置:   article > 正文

源码分析篇_SpringBoot源码分析_2 SpringBoot加载、启动和配置解析原理_springboot 加载starter 源码

springboot 加载starter 源码

源码分析篇_SpringBoot源码分析

源码分析篇_SpringBoot源码分析

2 SpringBoot加载、启动和配置解析原理

田超凡

原创博文,仿冒必究,部分素材转载自每特教育蚂蚁课堂

1 Spring Boot启动加载原理

1.1 Maven依赖

<parent>

        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-starter-parent</artifactId>

        <version>2.0.0.RELEASE</version>

    </parent>

 

    <dependencies>

 

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter</artifactId>

        </dependency>

 

        <!-- spring boot web -->

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-web</artifactId>

        </dependency>

        <dependency>

            <groupId>org.slf4j</groupId>

            <artifactId>slf4j-api</artifactId>

        </dependency>

 

        <dependency>

            <groupId>org.projectlombok</groupId>

            <artifactId>lombok</artifactId>

        </dependency>

 

        <!-- spring boot aop-->

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-aop</artifactId>

        </dependency>

        <dependency>

            <groupId>org.aspectj</groupId>

            <artifactId>aspectjweaver</artifactId>

            <version>1.9.3</version>

        </dependency>

 

        <dependency>

            <groupId>org.apache.commons</groupId>

            <artifactId>commons-lang3</artifactId>

        </dependency>

 

        <dependency>

            <groupId>com.alibaba</groupId>

            <artifactId>fastjson</artifactId>

            <version>1.2.73</version>

        </dependency>

 

        <!--  自定义YML配置项 -->

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-autoconfigure</artifactId>

        </dependency>

 

        <!--  能够给开发者引入该jar包后 有一定提示 -->

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-configuration-processor</artifactId>

        </dependency>

 

</dependencies>

 

 

1.2 启动类

/***

 * TODO TCF 启动类

 */

@SpringBootApplication

@Slf4j

public class App {

 

    public static void main(String[] args)

    {

        SpringApplication.run(App.class);

    }

 

}

 

1.3 @EnableAutoConfiguration加载spring-boot-starter和spring-boot-autoconfigurer依赖中的启动类

注意:

@SpringBootApplication是一个复合注解,基本原理等效于@Configuration + @EnableAutoConfiguration +@ComponentScan,@ComponentScan不指定扫包范围则默认扫描启动类所在目录及其子目录下的所有类,@EnableAutoConfiguration是加载springbootstarter外部依赖的核心注解

  1. 首先加载注解@SpringBootApplication中内嵌的@EnableAutoConfiguration

 

  1. 在@EnableAutoConfiguration中,通过@Import引入AutoConfigurationImportSelector选择器,该选择器就是加载SpringBoot外部依赖Bean的核心类

 

  1. AutoConfigurationImportSelector通过selectImports()引入外部依赖JAR包中的META-INF/spring.factories配置文件,springboot官方starter中都定义了改配置文件,该配置文件声明每个starter中定义的需要在SpringBoot项目启动时加载的类,通过EnableAutoConfiguration标记需要在此刻被SpringBoot加载的类

spring-boot-autoconfigurer包中的META-INF/spring.factories

 

AutoConfigurationImportSelector类 selectImports()方法,加载项目所有依赖中及当前项目自身中自定义的META-INF/spring.factories文件中的EnableAutoConfiguration映射的类

 

 

 

递归加载当前项目所有依赖包中的META-INF/spring.factories文件

 

加载spring.factories中定义的EnableAutoConfiguration映射的所有类

 

  1.  SpringApplication.run(App.class) 启动SpringBoot项目原理

 

 

底层是通过造一个SpringApplication实例来调用run()方法,那么我们也可以这样启动SpringBoot项目:new SpringApplication(primarySources).run(args);

也就是说,SpringApplication.run(Class<?> primarySource)

等效于new SpringApplication(Class<?> primarySource).run(args);

接下来看一下SpringApplication的构造函数做了什么:

 

在带参构造函数中,做了以下几件重要的事:

  1. 获取当前启动的项目类型webApplicationType

 

我们发现SpringBoot的Web应用类型有三个取值:NONE、SERVLET、REACTIVE

 

 

看下官方文档中这三个取值分别代表什么意思:

 

 

  1. 加载META-INF/spring.factories中定义的ApplicationContextInitializer

 

诶,觉得这个方法似曾相识?没错,它的原理就是同加载EnableAutoConfiguration原理一样的,都是同一个方法,都是先扫描当前项目所有依赖包中的META-INF/spring.factories文件,然后加载里面定义的类,只不过前面是加载EnableAutoConfiguration映射的类,此处是加载ApplicationContextInitializer映射的类

 

  1. 加载META-INF/spring.factories中定义的ApplicationListener,原理同上
  2. 获取启动类类型

 

 

到此,SpringApplication带参构造执行完毕,接下来看一下run方法的实现做了什么:

 

run方法主要做了以下几件事:

  1. 定义了一个方法内全局的StopWatch来监控run方法的执行状态和时间记录

 

  1. 获取META-INF/spring.factories定义的所有SpringApplicationRunListener监听器

 

  1. 递归调用每个SpringApplicationRunListener中定义的starting方法初始化监听器

 

 

 

 

  1. prepareEnvironment初始化环境,调用每个监听器的prepareEnvironment方法初始化环境

 

 

 

  1. 创建并初始化Banner组件,用于打印启动日志

 

 

 

 

  1. 创建SpringBoot集成Spring IOC容器的全局上下文AnnotationConfigServletWebServerApplicationContext,对应的应用类型webApplicationType是SERVLET

 

  1. 递归调用每个SpringApplicationRunListener的contextPrepared()初始化上下文

 

 

 

 

  1. refreshContext调用AbstractApplicationContext的refresh()初始化Spring IOC容器,原理见Spring源码分析-Spring IOC启动和加载原理总结

 

至此,SpringBoot启动和加载原理分析完毕

 

2 Spring Boot内嵌Tomcat启动原理

上文讲到,SpringBoot启动前会先加载

@EnableAutoConfiguration

  • @Import(AutoConfigurationImportSelector)
  • AutoConfigurationImportSelector selectImports()
  • Spring-autoconfigurer META-INF/spring.factories
  • EnableAutoConfiguration 映射的类

SpringBoot内嵌Tomcat的核心处理类就在EnableAutoConfiguration映射的在启动前被加载的类中,这个核心类就是ServletWebServerFactoryAutoConfiguration

 

 

 

通过@Import引入了三个内嵌的Web服务器配置类,这也意味着SpringBoot支持三种内嵌的Web服务器,分别是:Tomcat(默认,对应的webApplicationType是SERVLET)、Jetty、Undertow

 

3 Spring Boot集成SpringMVC原理

同上,META-INF/spring.factories EnableAutoConfiguration映射的类中,SpringMVC核心处理类是DispatcherServletAutoConfiguration

 

解析WebMvcProperties配置项并基于配置项创建并自动装载了默认的SpringMVC核心控制器DispatcherServlet

 

4 Spring Boot 集成Spring IOC原理

SpringBoot核心上下文AnnotationConfigServletWebServerApplicationContext

  • ServletWebServerApplicationContext
  • AbstractApplicationContext

 

SpringApplication run()

  • createApplicationContext ()
  • AnnotationConfigServletWebServerApplicationContext
  • refreshContext()
  • AnnotationConfigServletWebServerApplicationContext refreshContext()
  • ServletWebServerApplicationContext super.refresh()
  • AbstractApplicationContext refresh()   -- Spring IOC容器初始化,创建单例Bean并实现自动装配和初始化,详见Spring IOC原理学习总结

 

createApplicationContext()创建SpringBoot集成Spring IOC容器的全局上下文AnnotationConfigServletWebServerApplicationContext,对应的应用类型webApplicationType是SERVLET

 

 

 

refreshContext()初始化Spring IOC容器,创建并初始化单例Bean,实现自动装配

 

 

 

 

5 Spring Boot 配置解析和加载原理

SpringBoot自定义配置文件解析和加载依赖于SpringBoot核心监听器SpringApplicationRunListener,上文写到该监听器在SpringApplication.run()启动SpringBoot容器时会加载并调用其内部定义的多个事件方法,这些事件方法贯穿SpringBoot容器完整的启动周期(贯穿run方法的执行),现在就来看下这个核心监听器中定义的方法分别是在什么时机被调用的:

public interface SpringApplicationRunListener {

 

// 在run()方法开始执行时,该方法就立即被调用,可用于在初始化最早期时做一些工作

void starting();

 

    // 当environment构建完成,ApplicationContext创建之前,该方法被调用

    void environmentPrepared(ConfigurableEnvironment environment);

   

// 当ApplicationContext构建完成时,该方法被调用

    void contextPrepared(ConfigurableApplicationContext context);

   

// 在ApplicationContext完成加载,但没有被刷新前,该方法被调用

    void contextLoaded(ConfigurableApplicationContext context);

   

// 在ApplicationContext刷新并启动后,CommandLineRunners和ApplicationRunner未被调用前,该方法被调用

void started(ConfigurableApplicationContext context);

 

    // 在run()方法执行完成前该方法被调用

    void running(ConfigurableApplicationContext context);

   

// 当应用运行出错时该方法被调用

void failed(ConfigurableApplicationContext context, Throwable exception);

 

}

 

6 自定义SpringApplicationRunListener监听器解析自定义配置文件

  1. 自定义配置文件my.properties:

kid.tcf.userName = tcf

  1. 自定义监听器MyEnvironmentApplicationListener,实现SpringApplicationRunListener,声明带参构造,且参数类型需和加载类型匹配

public class MyEnvironmentApplicationListener implements SpringApplicationRunListener {

 

    private SpringApplication application;

    private String[] args;

 

    public MyEnvironmentApplicationListener(SpringApplication application, String[] args)

    {

        this.application = application;

        this.args = args;

    }

 

    @Override

    public void starting()

    {

        System.out.println("====== starting ======");

    }

 

    @Override

    public void environmentPrepared(ConfigurableEnvironment environment)

    {

 

        Properties properties = new Properties();

        try

        {

            properties.load(this.getClass().getClassLoader().getResourceAsStream("my.properties"));

 

            PropertySource propertySource = new PropertiesPropertySource("my", properties);

            environment.getPropertySources().addLast(propertySource);

        }

        catch (IOException e)

        {

            e.printStackTrace();

        }

 

    }

 

    @Override

    public void contextPrepared(ConfigurableApplicationContext context) {

 

    }

 

    @Override

    public void contextLoaded(ConfigurableApplicationContext context) {

 

    }

 

    @Override

    public void started(ConfigurableApplicationContext context) {

 

    }

 

    @Override

    public void running(ConfigurableApplicationContext context) {

 

    }

 

    @Override

    public void failed(ConfigurableApplicationContext context, Throwable exception) {

 

    }

}

 

  1. 新增META-INF/spring.factories,将自定义监听器映射到SpringApplicationRunListener,便于SpringBoot容器在启动时加载(SpringApplication.run() -> loadListeners())

 

注意:自定义SpringApplicationRunListener必须带有带参构造函数,且类型必须是SpringApplication和String[],否则无法启动时被加载,原因是:

SpringApplication.run()加载SpringApplicationRunListener监听器时,是基于反射和监听器实现类中的带参构造创建监听器实现类实例并实现方法调用:

 

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/552787
推荐阅读
相关标签
  

闽ICP备14008679号