zoukankan      html  css  js  c++  java
  • SpringBoot源码修炼—系统初始化器

    SpringBoot源码修炼—系统初始化器

    传统SSM框架与SpringBoot框架简要对比

    SSM搭建流程缺点:耗时长,配置文件繁琐,需要找合适版本的jar包
    缺点:

    1. 耗时长
    2. 配置文件繁琐
    3. 需要找合适版本的jar包

    SpringBoot搭建流程
    在这里插入图片描述优点

    1. 耗时短
    2. 配置文件简洁
    3. 不关注版本管理

    一、系统初始化器实践

    • 类名:ApplicationContextInitializer
    • 介绍:Spring容器刷新之前执行的一个回调函数
    • 作用:向SpringBoot容器中注册属性
    • 使用:继承接口自定义实现

    创建系统初始化器方式一

    (1)创建初始化器(在包initializer下创建FirstInitializer)

    /**
     *
     * 第一个系统初始化器
     */
    @Order(1)
    public class FirstInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    
        @Override
        public void initialize(ConfigurableApplicationContext applicationContext) {
    
            ConfigurableEnvironment environment = applicationContext.getEnvironment();
    
            Map<String, Object> map = new HashMap<>();
            map.put("key1", "value1");
    
            MapPropertySource mapPropertySource = new MapPropertySource("firstInitializer", map);
    
            environment.getPropertySources().addLast(mapPropertySource);
    
            System.out.println("run firstInitializer");
    
        }
    }
    

    (2)创建spring.factories
    内容为:org.springframework.context.ApplicationContextInitializer=com.mooc.springboot2.initializer.FirstInitializer

    (3)创建服务类TestService

    @Component
    public class TestService implements ApplicationContextAware {
    
        private ApplicationContext applicationContext;
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            this.applicationContext = applicationContext;
        }
    
        public String test() {
            return applicationContext.getEnvironment().getProperty("key1");
        }
    
    }
    

    (4)在Controller添加测试方法调用

    @Controller
    @RequestMapping("/demo")
    public class DemoController {
        @Autowired
        private TestService testService;
    
        @RequestMapping("test")
        @ResponseBody
        public String test() {
            return testService.test();
        }
    }
    

    (5)启动工程查看效果
    在这里插入图片描述
    返回值是之前设置的value1
    在这里插入图片描述 创建系统初始化器方式二

    (1)创建初始化器

    @Order(2)
    public class SecondInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
        @Override
        public void initialize(ConfigurableApplicationContext applicationContext) {
    
            ConfigurableEnvironment environment = applicationContext.getEnvironment();
    
            Map<String, Object> map = new HashMap<>();
            map.put("key2", "value2");
    
            MapPropertySource mapPropertySource = new MapPropertySource("secondInitializer", map);
    
            environment.getPropertySources().addLast(mapPropertySource);
    
            System.out.println("run secondInitializer");
        }
    }
    

    (2)启动类修改

    @SpringBootApplication
    @MapperScan("com.mooc.demo.mapper")
    public class Springboot2Application{
     
        public static void main(String[] args) {
            //SpringApplication.run(Springboot2Application.class, args);
            SpringApplication springApplication = new SpringApplication(Springboot2Application.class);
            springApplication.addInitializers(new SecondInitializer());
            springApplication.run(args);
        }
    }
    

    (3) 修改TestService的test方法

    public String test(){
         return  applicationContext.getEnvironment().getProperty("key2");
     }
    

    (4)启动工程查看效果
    在这里插入图片描述
    说明两个初始化器都已经创建了
    返回值为value2
    在这里插入图片描述
    创建系统初始化器方式三

    (1)创建初始化器

    @Order(3)
    public class ThirdInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
        @Override
        public void initialize(ConfigurableApplicationContext applicationContext) {
    
            ConfigurableEnvironment environment = applicationContext.getEnvironment();
    
            Map<String, Object> map = new HashMap<>();
            map.put("key3", "value3");
    
            MapPropertySource mapPropertySource = new MapPropertySource("thirdInitializer", map);
    
            environment.getPropertySources().addLast(mapPropertySource);
    
            System.out.println("run thirdInitializer");
        }
    }
    

    (2)在application.properties 添加

    context.initializer.classes=com.example.demo.initializer.ThirdInitializer
    

    在这里插入图片描述
    (3) 修改TestService的test方法

    public String test(){
         return applicationContext.getEnvironment().getProperty("key3");
     }
    

    (4)启动工程查看效果在这里插入图片描述
    可以发现打印的顺序和定义的不一致,第三个ThirdInitializer初始化器优先于第一个和第二个打印出来。

    返回值为value3
    在这里插入图片描述
    springboot初始化器是如何被识别的呢?

    本质上靠的就是SpringFactoriesLoader

    二、SpringFactoriesLoader 介绍

    框架内部使用的通用工厂加载机制
    从classpath下多个jar包特定的位置读取文件并初始化类
    文件内容必须是kv形式,即properties类型
    key是全限定名(抽象类|接口)、value是实现,多个实现用逗号分隔

    作用:SpringBoot框架从类路径所有jar包中读取特定文件实现扩展类的载入

    查看源码

    从启动类进入

    SpringApplication.run(Sb2Application.class, args);
    

    1、进入run方法

    public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
        return run(new Class<?>[] { primarySource }, args);
    }
     
         
    public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
        return new SpringApplication(primarySources).run(args);
    }
         
    public SpringApplication(Class<?>... primarySources) {
        this(null, primarySources);
    }
    

    2、进入SpringApplication构造函数

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = deduceMainApplicationClass();
    }
    

    3、进入setInitializers方法

    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    

    通过getSpringFactoriesInstances获取系统初始化器的一些实现

    4、进入getSpringFactoriesInstances方法
    在这里插入图片描述
    通过loadFactoryNames这个方法去获得所有系统初始化器实现类的全路径名,再通过createSpringFactoriesInstances去获得它们的实例,接着通过AnnotationAwareOrderComparator对它们进行排序。

    5、进入loadFactoryNames方法
    在这里插入图片描述

    6、进入loadSpringFactories方法
    在这里插入图片描述可以发现FACTORIES_RESOURCE_LOCATION的值为"META-INF/spring.factories"。 这就是为什么我们前面要在META-INF文件夹下创建spring.factories 文件的原因。

    获得所有Jar包下的META-INF/spring.factories文件

    通过以上过程,将初始化器实现的类名注入到容器当中
    如下图,debug显示所有系统初始化器
    在这里插入图片描述

    7、实例化这些初始化器
    在这里插入图片描述

    8、通过setInitializers将初始化器实例注入到spring容器中在这里插入图片描述

    loadSpringFactories整体流程
    在这里插入图片描述

    三、系统初始化器原理

    探究系统初始化器是如何被调用以及被调用的原理

    经过对官方的翻译,它的意思大致如下:

    • 上下文刷新即spring的refresh方法前调用
    • 用来编码设置一些属性变量通常用在web环境中
    • 可以通过order接口进行排序

    springboot大致流程如下图所示,其中在准备上下文过程中,会遍历调用Initalizer的initalize方法
    在这里插入图片描述

    系统初始化器方式一实现原理

    进入run方法查看源码
    在这里插入图片描述

    再进入准备上下文的方法prepareContext中,可以看到调用初始化器部分
    在这里插入图片描述

    再次进入,这里遍历所有系统初始化器,并调用对应的初始化器的initialize方法( getInitializers返回所有的系统初始化器)
    在这里插入图片描述

    系统初始化器方式二实现原理

    这里通过new一个SpringApplication,添加我们自定义的初始化器
    在这里插入图片描述

    我们知道在SpringApplication初始化之后,系统初始化器已经设置过了(通过setInitializers方法)在这里插入图片描述

    同时SpingApplication实例提供了addInitializers方法来帮助我们增加自定义的初始化器
    在这里插入图片描述
    再通过springApplication.run(args)的run方法(和方式一的run方法是同一个)就能保证添加的系统初始化器能够在后面的run方法中正常执行

    系统初始化器方式三实现原理

    通过在application.properties 配置文件中添加配置context.initializer.classes=com.mooc.demo.initializer.ThirdInitializer来实现

    这里主要是通过DelegatingApplicationContextInitializer初始化器来实现的
    在这里插入图片描述可以看到DelegatingApplicationContextInitializer里的order=0,也就是说这个初始化器最先被调用

    在spring的spring.factories文件中有这个初始化器,在加载系统初始化器的时候被加载。
    在这里插入图片描述

    三种方式实现初始化器的原理

    1. 定义在spring.factories 文件中被SpringFactoriesLoader发现注册
    2. 初始化完毕后手动添加
    3. 定义成环境变量被DelegatingApplicationContextInitializer发现注册

    欢迎关注我的公众号!里面可以加入微信技术交流群!
  • 相关阅读:
    2019年中国高校计算机大赛
    2019年华北五省(市、自治区)及港澳台大学生计算机应用大赛
    2019年(第12届)中国大学生计算机设计大赛
    2020移动开发竞赛
    2019年华北五省(市、自治区)及港澳台大学生计算机应用大赛
    Android 开发者指南
    Android :HangUp Notification 横幅通知
    HBU E-mobile
    Android Q is Android 10
    Android Studio 3.5(Last updated 7/29/2019)
  • 原文地址:https://www.cnblogs.com/1ssqq1lxr/p/14522524.html
Copyright © 2011-2022 走看看