• 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<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    //3. 利用工厂加载
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
    //4. 得到所有组件(127个AutoConfiguration组件)
    Map<String, List<String>> 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<Bootstrapper>),getSpringFactoriesInstances()META-INF/spring.factories文件中找Bootstrapper

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

          List<ApplicationContextInitializer<?>> 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. 实例化 所有剩下的非懒加载的实例对象

     

  • 相关阅读:
    Ubuntu 20.04安装搜狗输入法
    修改Npm为淘宝镜像地址
    共享打印机连接报错0x0000011b
    echarts柱状图上方显示数据
    openlayers 图层控制
    openlayers+geoserver 从发布地图到点击查询、属性查询
    Openlayers中的比例尺(scale)和分辨率(resolution)
    JavaScript中let和var和const的区别
    css 居中 一个。
    mysql常用函数 取逗号分隔第一个词
  • 原文地址:https://www.cnblogs.com/yifanSJ/p/16022437.html
Copyright © 2020-2023  润新知