SpringBoot启动原理


一、依赖管理

Maven

二、自动配置

1. 自动配好Tomcat

  • 引入Tomcat依赖
  • 配置Tomcat

2.自动配好SpringMVC

  • 引入SpringMVC全套组件,如DispatcherServlet、HandlerMapping、Controller、ModelAndView、ViewResolver
  • 自动配好SpringMVC常用组件

3. 自动配好Web常见功能,如字符编码,文件上传、视图解析器

  • SpringBoot帮我们配置好了所有 web开发的常见场景

4. 默认的包结构

  • 主程序所在包及其下面的所有子包里面的组件都会被默认扫描进来
  • 无需以前的包扫描配置
  • 想要改变扫描路径,@SpringBootApplication(scanBasePackages = "com.atguigu")
    • 或者@ComponentScan指定扫描路径
@SpringBootApplication
等同于
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.atguigu.boot")
  • 各种配置拥有默认值 (在application.properties)
    • 默认的值最终都是映射到某个类上MultipartProperties
    • 配置文件的值最终会绑定每个类上,这个类会在容器中创建对象
  • 按需加载所有自动配置项
    • 非常多的starter
    • 引入了哪些场景这个场景 的自动配置才会开启
    • SpringBoot所有的自动配置功能都在 spring-boot-autoconfigure 包里面

三、容器功能

(一)底层注解

1、@Configuration

  • 配置类里面使用@Bean标注在方法上给容器注册组件,默认也是单实例的
  • 配置类本身也是组件
  • proxyBeanMethods:代理bean的方法,默认在true【解决组件依赖】
    • Full(全配置配置 proxyBeanMethods=true)
    • Lite(轻量级配置 proxyBeanMethods=false)

2、@Bean、@Component、@Controller、@Service、@Repository

3、@ComponentScan、@Import

4、@Conditional 条件装配注解

//容器中有tom组件时,才创建user01组件
@ConditionalOnBean(name = "tom")
@Bean
public User user01(){
    User zhangsan = new User("zhangsan", 18);
    zhangsan.setPet(tomecatPet());
    return zhangsan;
}

5、@ImportResource

//导入xml标签形式声明Bean(老方法)的配置
@ImportResource("classpath:beans.xml")

6. @ConfigurationProperties 或 @EnableConfigurationProperties(Car.class)

@ConfigurationProperties(prefix = "mycar")
@Component
public class Car {
    private String brand;
    private Integer price;
}

@EnableConfigurationProperties(Car.class)
public class DemoApplication {
}

(二)@SpringBootApplication 核心注解讲解

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
}

1. @SpringBootConfiguration

@Configuration 代表当前为配置类

2. @ComponentScan

指定扫描哪些

3. @EnableAutoConfiguration

@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {}

(1) 自动包规则原理

@AutoConfigurationPackage

自动配置包

// 给容器中导入一个组件
@Import({Registrar.class}) public @interface AutoConfigurationPackage {}
//利用Registrar给容器中导入一系列组件
//将指定一个包下的所有组件导入进来,MainApplication.java所在包下!!!!!!!
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));

(2) 初始加载自动配置类

@Import(AutoConfigurationImportSelector.class)

// 1. 利用这个 方法给容器中导入一些组件
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
//2. 调用该方法获取所有需要导入到容器中的配置类
List configurations = getCandidateConfigurations(annotationMetadata, attributes);
//3. 利用工厂加载
List configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
//4. 得到所有组件(127个AutoConfiguration组件)
Map, List> loadSpringFactories(ClassLoader classLoader)
//5. 从META-INF/spring.factories位置来加载一个文件
默认扫描我们当前系统里面所有 META-INF/spring.factories 位置的文件
spring-boot-autoconfigure-2.5.0.jar包里面也有META-INF/spring.factories

文件里面写死了spring-boot一启动就要给容器中加载的所有配置类

(2.1) 按需配置

虽然我们127个场景的所有自动配置启动的时候默认全部加载。
最终会按需配置

@Configuration(proxyBeanMethods = false) @ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true) public class AopAutoConfiguration { @Configuration(proxyBeanMethods = false) @ConditionalOnClass(Advice.class) // 按需加载,有aspectj相关组件,才会加载该 AopAutoConfiguration!!!!!!!!!!! static class AspectJAutoProxyingConfiguration { } }

(2.2) 加载配置文件yml

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {
    @Configuration(proxyBeanMethods = false)
    @Conditional(DefaultDispatcherServletCondition.class)
    @ConditionalOnClass(ServletRegistration.class)
    @EnableConfigurationProperties(WebMvcProperties.class)  // 这里会加载spring.mvc相关配置
    protected static class DispatcherServletConfiguration {
    }
}
@ConfigurationProperties(prefix = "spring.mvc")
public class WebMvcProperties {}
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(ServerProperties.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(CharacterEncodingFilter.class)
@ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {
}

@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
    /**
     * Server HTTP port.
     */
    private Integer port;

    /**
     * Network address to which the server should bind.
     */
    private InetAddress address;
}

(2.3) SpringBoot会在底层配好所有的组件,但是如果用户自己配置了,以用户的优先

// 系统自带
@Bean
@ConditionalOnMissingBean
public CharacterEncodingFilter characterEncodingFilter() {
    CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
    filter.setEncoding(this.properties.getCharset().name());
    filter.setForceRequestEncoding(this.properties.shouldForce(Encoding.Type.REQUEST));
    filter.setForceResponseEncoding(this.properties.shouldForce(Encoding.Type.RESPONSE));
    return filter;
}

//用户不想用系统的,也可重写
@Bean
public CharacterEncodingFilter myFile() {
return }

总结:

  • SpringBoot先加载所有的自动配置类 xxxAutoConfiguration
  • 每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值,xxxProperties里面拿,xxxProperties和配置文件 进行绑定
  • 生效的配置类就会给容器中装配很多组件
  • 只要容器中有这些组件,相当于这些功能就有了
  • 定制化配置
    • 用户直接自己@Bean替换底层的组件
    • 用户去看这个组件是获取的配置文件什么值就去修改

从xxxxAutoConfiguration ---> 导入很多组件 ---> EnableConfigurationProperties ---> 组件从xxxProperties里面拿值 ----> 从application.properties配置文件中获取

四、SpringBoot启动过程

1. 创建SpringApplication

(1) 保存一些信息。

(2) 判定当前应用的类型。ClassUtils。Servlet

(3) 找bootstrappers;初始启动引导器(List),getSpringFactoriesInstances()META-INF/spring.factories文件中找Bootstrapper

(4) 找ApplicationContextInitializer;初始化器;getSpringFactoriesInstances()META-INF/spring.factories文件中找ApplicationContextInitializer

      List> initializers

(5) 找ApplicationListener;应用监听器;getSpringFactoriesInstances()META-INF/spring.factories文件中找ApplicationListener

(6) 找到主程序

2. 运行SpringApplication

(1) StopWatch

     记录应用的启动时间

(2) 创建引导上下文(Context环境)createBootstrapContext()

  • 获取所有之前 bootstrappers 挨个执行 intitialize() 来完成对引导启动器上下文环境设置
public interface Bootstrapper {

    /**
     * Initialize the given {@link BootstrapRegistry} with any required registrations.
     * @param registry the registry to initialize
     */
    void intitialize(BootstrapRegistry registry);

}

(3) 让当前应用进入headless模式,java.awt.headless

(4) 获取所有RunListener(运行监听器)【为了方便所有 Listener进行事件感知】

  • getSpringFactoriesInstances() META-INF/spring.factories文件中找SpringApplicationRunListener

(5) 遍历 SpringApplicationRunListener 调用listener.starting() 方法;【刚创建完上下文容器的基本信息,就调用starting,代表容器准备启动了!!!!!!!】

  • 相当于通知所有感兴趣系统正在启动过程的人,项目正在starting.

(6) 保存命令行参数: ApplicationArguments

(7) 准备环境 prepareEnvironment()

  • 返回或创建一个基础环境信息对象 StandardServletEnvironment
  • 配置环境信息对象。
    • 读取所有的配置源的配置属性值
  • 绑定环境信息
  • 监听器调用 listener.environmentPrepared();通知所有的监听器当前环境准备完成【环境信息准备完成后调用,代表环境信息准备完成了!!!!!!!】

(8) 创建IOC容器 createApplicationContext()

  • 根据项目类型(Servlet)创建容器
  • 当前会创建 AnnotationConfigServletWebServerApplicationContext

(9) 准备ApplicationContext IOC容器的基本信息 prepareContext()

  • 保存环境信息
  • IOC容器的后置处理流程
  • 应用初始化器, applyInitializers
    • 遍历所有的ApplicationContextInitializer,调用initialize() 来对ioc容器进行初始化扩展功能。
    • 遍历所有的调用 listener.contextPrepared()。EvenpublishRunListener 通知所有的监听器contextPrepared【IOC容器准备完成后调用可以开始注册组件、获取组件!!!!!!!】

  • 通知所有的监听器,调用 listener.contextLoaded(),通知所有监听器 contextLoaded;【IOC容器加载完成后调用!!!!!!!!!!!】

(10) 刷新 IOC容器 refreshContext

  • 创建容器中的所有组件(需要补充学习)

(11) 容器刷新完成后工作 afterRefresh

(12) 所有监听器调用 listeners.started(context); 通知所有的监听器started【IOC容器开始启动调用这里容器已完成刷新,且已创建实例!!!!!!!】

(13) 调用所有的runners callRunners()

  • 获取容器中的ApplicationRunner
  • 获取容器中的CommandLineRunner
  • 获取所有runner并且按照@Order进行排序
  • 遍历所有的runner,调用run方法
  • 如果以上有异常
    • 调用Listener的falid

(14) 调用所有监听器的running方法 listeners.running(context); 通知所有的监听器running【IOC容器启动完成后调用这里容器已启动完成!!!!!!!】

  • running如果有问题,继续通知faild,调用所有Listener的faile, 通知所有的监听器faild

五大组件

1. ApplicationContextInitializer 初始化器

.initialize();

2. ApplicationListener 应用监听器

.onApplicationEvent()

3. SpringApplicationRunListener 运行监听器

.starting()

.environmentPrepared()

.contextPrepared()

.contextLoaded()

.started

.running

4. ApplicationRunner

.run()

5. CommandLineRunner

.run()

 刷新 IOC容器 refreshContext 创建容器中的所有组件(需要补充学习):

1. 初始化准备

2. 创建Bean工厂ConfigurableListableBeanFactory ,并获取Bean定义信息

3. 初始化Bean工厂prepareBeanFactory

4. 在bean实例化后创建之前调用BeanFactoryPostProcessors后置处理方法

5. 注册并实例化所有BeanPostProcessors

6. 国际化

7. 进行相关事件发布

8. OnRefresh() —— 创建内置的Servlet容器 SpringBoot Tomcat在这里启动

9. 注册应用监听器

10. 实例化 所有剩下的非懒加载的实例对象