美文网首页SpringBoot源码解析
SpringBoot系统初始化器

SpringBoot系统初始化器

作者: lanmi17 | 来源:发表于2020-06-26 13:12 被阅读0次

    介绍

    类名

    org.springframework.context.ApplicationContextInitializer
    

    Spring容器刷新之前执行的回调函数

    作用

    向Spring容器中注册属性(SpringBoot留的一个扩展点,用于添加自定义属性值)

    实现方式

    方式一

    
    1. // 实现 ApplicationContextInitializer 接口
       // FirstInitializer.java
    @Slf4j
    @Order(1)
    public class FirstInitializer implements ApplicationContextInitializer {
        @Override
        public void initialize(ConfigurableApplicationContext applicationContext) {
            ConfigurableEnvironment environment = applicationContext.getEnvironment();
            Map<String,Object> map = new HashMap<>();
            map.put("first","value1");
            MapPropertySource propertySource = new MapPropertySource("first",map);
            environment.getPropertySources().addLast(propertySource);
            log.info("first initializer done!");
        }
    }
    2. // resource 目录下新建 META-INF文件夹,然后新建spring.factories 文件(之后讲解原因)
     org.springframework.context.ApplicationContextInitializer=上边新建类FirstInitializer的全路径名
    

    接下来采用 environment 验证:

        private final Environment environment;
    
        public InitializerController(Environment environment) {
            this.environment = environment;
        }
    
        @GetMapping("/{key}")
        public ResponseEntity<String> initializer(@PathVariable String key){
            try {
                log.info("属性:{},对应value为:{}",key,Optional.ofNullable(environment.getProperty(key)).orElse("属性不存在"));
            } catch (Exception e){
                e.printStackTrace();
            }
            return new ResponseEntity<>(HttpStatus.OK);
        }
    

    输出结果:属性:first,对应value为:value1

    方式二

    采用硬编码的方式,在启动类主动注册

    //  SecondInitializer.java
    public class SecondInitializer implements ApplicationContextInitializer {
        @Override
        public void initialize(ConfigurableApplicationContext applicationContext) {
            ConfigurableEnvironment environment = applicationContext.getEnvironment();
            Map<String,Object> map = new HashMap<>();
            map.put("second","value2");
            MapPropertySource propertySource = new MapPropertySource("first",map);
            environment.getPropertySources().addLast(propertySource);
            log.info("second initializer done!");
        }
    }
    // 启动类
    public class SbCodeApplication {
    
        public static void main(String[] args) {
            SpringApplication application = new SpringApplication(SbCodeApplication.class);
            application.addInitializers(new SecondInitializer());
            application.run(args);
        }
    
    }
    

    使用方式1的验证,同样可以获取到预期的结果:属性:second,对应value为:value2

    方式三

    application.yml 文件中指定 context.initializer.classes : 自定义初始化器全路径,使用方式1的验证,同样可以获取到预期的结果:属性:third,对应value为:value3

    方式对比

    日志:

    INFO 6364 --- [           main] c.l.s.code.Initializer.ThirdInitializer  : third initializer done!
    INFO 6364 --- [           main] c.l.s.code.Initializer.FirstInitializer  : first initializer done!
    INFO 6364 --- [           main] c.l.s.c.Initializer.SecondInitializer    : second initializer done!
    

    第三种方式,在application.yml配置文件中指定自定义初始化器,优先级最高

    原理

    加载机制

    Debug SpringApplication.java


    SpringApplication.java
        private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
            ClassLoader classLoader = getClassLoader();
            //  <1> 使用SpringFactoryLoader 获取所有的实现初始化器的类名
            Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
            // <2> 获取类对应的实例对象
            List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
            // <3> 排序
            AnnotationAwareOrderComparator.sort(instances);
            return instances;
        }
    

    SpringFactoriesLoader

    介绍:从classpath下多个jar包特定位置读取并初始化类,实现通用工厂类加载,实现扩展类加载
    工作原理

    // 对应以上<1>的过程
    public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
            String factoryTypeName = factoryType.getName();
            // 获取类名为 factoryType的所有实现类的String
            return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
        }
    
        private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
            // 从缓存获取
            MultiValueMap<String, String> result = cache.get(classLoader);
            if (result != null) {
                return result;
            }
    
            try {
                // 扫描classpath路径下所有的文件: "META-INF/spring.factories"
                Enumeration<URL> urls = (classLoader != null ?
                        classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                        ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
                result = new LinkedMultiValueMap<>();
                while (urls.hasMoreElements()) {
                    URL url = urls.nextElement();
                    // 将spring.factories 转为UrlResource
                    UrlResource resource = new UrlResource(url);
                    // resource 转为可处理的kv对象
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    for (Map.Entry<?, ?> entry : properties.entrySet()) {
                        String factoryTypeName = ((String) entry.getKey()).trim();
                        // 处理为 <factoryTypeName,List<实现类路径>>
                        for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                            result.add(factoryTypeName, factoryImplementationName.trim());
                        }
                    }
                }
                //进行缓存
                cache.put(classLoader, result);
                return result;
            }
            catch (IOException ex) {
                throw new IllegalArgumentException("Unable to load factories from location [" +
                        FACTORIES_RESOURCE_LOCATION + "]", ex);
            }
        }
    
        // 对应以上 <2> 的过程
        private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
                ClassLoader classLoader, Object[] args, Set<String> names) {
            List<T> instances = new ArrayList<>(names.size());
            for (String name : names) {
                try {
                    Class<?> instanceClass = ClassUtils.forName(name, classLoader);
                    Assert.isAssignable(type, instanceClass);
                    Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
                    T instance = (T) BeanUtils.instantiateClass(constructor, args);
                    instances.add(instance);
                }
                catch (Throwable ex) {
                    throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
                }
            }
            return instances;
        }
    

    通过以上<1><2><3> 获取所有实现初始化器的实例对象

    调用时机

    获取到实例对象以后,如何使用呢?Debug SpringApplication.run(SbCodeApplication.class,args) run方法

    exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
            new Class[] { ConfigurableApplicationContext.class }, context);
            // 准备上下文
    prepareContext(context, environment, listeners, applicationArguments, printedBanner);
    refreshContext(context);
    afterRefresh(context, applicationArguments);
    
    private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
                SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
            context.setEnvironment(environment);
            postProcessApplicationContext(context);
            // 加载初始化器实现
            applyInitializers(context);
            listeners.contextPrepared(context);
            
            ......
            }
    
    // 循环遍历初始化器实现
    protected void applyInitializers(ConfigurableApplicationContext context) {
        for (ApplicationContextInitializer initializer : getInitializers()) {
            Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
                    ApplicationContextInitializer.class);
            Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
            initializer.initialize(context);
        }
    }
    

    相关文章

      网友评论

        本文标题:SpringBoot系统初始化器

        本文链接:https://www.haomeiwen.com/subject/anzyfktx.html