zoukankan      html  css  js  c++  java
  • ApplicationContextInitializer的理解和使用

    一、ApplicationContextInitializer 介绍

    1.1 作用

    ApplicationContextInitializer 接口用于在 Spring 容器刷新之前执行的一个回调函数,通常用于向 SpringBoot 容器中注入属性。

    1.2 内置实现类

    DelegatingApplicationContextInitializer

    使用环境属性 context.initializer.classes 指定的初始化器(initializers)进行初始化工作,如果没有指定则什么都不做。
    
    通过它使得我们可以把自定义实现类配置在 application.properties 里成为了可能。
    

    ContextIdApplicationContextInitializer

    设置Spring应用上下文的ID,会参照环境属性。至于Id设置为什么值,将会参考环境属性:
    * spring.application.name
    * vcap.application.name
    * spring.config.name
    * spring.application.index
    * vcap.application.instance_index
        
    如果这些属性都没有,ID 使用 application。
    

    ConfigurationWarningsApplicationContextInitializer

    对于一般配置错误在日志中作出警告
    

    ServerPortInfoApplicationContextInitializer

     将内置 servlet容器实际使用的监听端口写入到 Environment 环境属性中。这样属性 local.server.port 就可以直接通过 @Value 注入到测试中,或者通过环境属性 Environment 获取。
    

    SharedMetadataReaderFactoryContextInitializer

    创建一个 SpringBoot和ConfigurationClassPostProcessor 共用的 CachingMetadataReaderFactory对象。实现类为:ConcurrentReferenceCachingMetadataReaderFactory
    

    ConditionEvaluationReportLoggingListener

    将 ConditionEvaluationReport写入日志。
    

    二、实现方式

    首先新建三个自定义类,实现 ApplicationContextInitializer 接口

    public class FirstInitializer implements ApplicationContextInitializer {
    
        @Override
        public void initialize(ConfigurableApplicationContext applicationContext) {
            ConfigurableEnvironment environment = applicationContext.getEnvironment();
    
            Map<String, Object> map = new HashMap<>();
            map.put("key1", "First");
    
            MapPropertySource mapPropertySource = new MapPropertySource("firstInitializer", map);
            environment.getPropertySources().addLast(mapPropertySource);
    
            System.out.println("run firstInitializer");
        }
    
    }
    
    public class SecondInitializer implements ApplicationContextInitializer {
    
        @Override
        public void initialize(ConfigurableApplicationContext applicationContext) {
            ConfigurableEnvironment environment = applicationContext.getEnvironment();
    
            Map<String, Object> map = new HashMap<>();
            map.put("key1", "Second");
    
            MapPropertySource mapPropertySource = new MapPropertySource("secondInitializer", map);
            environment.getPropertySources().addLast(mapPropertySource);
    
            System.out.println("run secondInitializer");
        }
    
    }
    
    public class ThirdInitializer implements ApplicationContextInitializer {
    
        @Override
        public void initialize(ConfigurableApplicationContext applicationContext) {
            ConfigurableEnvironment environment = applicationContext.getEnvironment();
    
            Map<String, Object> map = new HashMap<>();
            map.put("key1", "Third");
    
            MapPropertySource mapPropertySource = new MapPropertySource("thirdInitializer", map);
            environment.getPropertySources().addLast(mapPropertySource);
    
            System.out.println("run thirdInitializer");
        }
    
    }
    

    2.1 在 resources/META-INF/spring.factories 中配置

    org.springframework.context.ApplicationContextInitializer=com.learn.springboot.initializer.FirstInitializer
    

    2.2 在 mian 函数中添加

    @SpringBootApplication
    public class SpringbootApplication {
    
        public static void main(String[] args) {
    //        SpringApplication.run(SpringbootApplication.class, args);
            SpringApplication springApplication = new SpringApplication(SpringbootApplication.class);
            springApplication.addInitializers(new SecondInitializer());
            springApplication.run();
        }
    
    }
    

    2.3 在配置文件中配置

    context.initializer.classes=com.learn.springboot.initializer.ThirdInitializer
    

    运行项目,查看控制台:

      .   ____          _            __ _ _
     /\ / ___'_ __ _ _(_)_ __  __ _    
    ( ( )\___ | '_ | '_| | '_ / _` |    
     \/  ___)| |_)| | | | | || (_| |  ) ) ) )
      '  |____| .__|_| |_|_| |_\__, | / / / /
     =========|_|==============|___/=/_/_/_/
     :: Spring Boot ::        (v2.2.5.RELEASE)
    
    run thirdInitializer
    run firstInitializer
    run secondInitializer
    

    可以看到配置生效了,并且三种配置优先级不一样,配置文件优先级最高,spring.factories 其次,代码最后。

    三、获取属性值

    @RestController
    public class HelloController {
        
        private ApplicationContext applicationContext;
        
        public HelloController(ApplicationContext applicationContext) {
            this.applicationContext = applicationContext;
        }
    
        @RequestMapping("/getAttributes")
        public String getAttributes() {
            String value = applicationContext.getEnvironment().getProperty("key1");
            System.out.println(value);
            return value;
        }
        
    }
    

    启动项目,访问http://localhost:8080/getAttributes 查看控制台输出:

    Third
    

    发现同名的 key,只会存在一个,并且只存第一次设置的值。

    四、通过 @Order 注解修改执行顺序

    注:@order 值越小,执行优先级越高

    4.1 不同配置方式下,执行顺序

    @Order(1)
    public class SecondInitializer implements ApplicationContextInitializer {
        ......
    }
    
    @Order(2)
    public class FirstInitializer implements ApplicationContextInitializer {
        ......
    }
    
    @Order(3)
    public class ThirdInitializer implements ApplicationContextInitializer {
        ......
    }
    

    运行项目,查看控制台:

      .   ____          _            __ _ _
     /\ / ___'_ __ _ _(_)_ __  __ _    
    ( ( )\___ | '_ | '_| | '_ / _` |    
     \/  ___)| |_)| | | | | || (_| |  ) ) ) )
      '  |____| .__|_| |_|_| |_\__, | / / / /
     =========|_|==============|___/=/_/_/_/
     :: Spring Boot ::        (v2.2.5.RELEASE)
    
    run thirdInitializer
    run secondInitializer
    run firstInitializer
    

    可以看到通过 @Order ** 注解是可以改变spring.factories** 和代码形式的执行顺序的,但是application.properties 配置文件的优先级还是最高的。

    4.2 同一配置下,执行顺序

    新建实现类

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

    application.properties 文件中配置

    context.initializer.classes=com.learn.springboot.initializer.ThirdInitializer,com.learn.springboot.initializer.FourthInitializer
    

    运行项目,查看控制台:

      .   ____          _            __ _ _
     /\ / ___'_ __ _ _(_)_ __  __ _    
    ( ( )\___ | '_ | '_| | '_ / _` |    
     \/  ___)| |_)| | | | | || (_| |  ) ) ) )
      '  |____| .__|_| |_|_| |_\__, | / / / /
     =========|_|==============|___/=/_/_/_/
     :: Spring Boot ::        (v2.2.5.RELEASE)
    
    run fourthInitializer
    run thirdInitializer
    run secondInitializer
    run firstInitializer
    

    可以看到同一配置方式, @Order 注解也可以起作用。

    五、系统初始化器原理解析

    5.1在 resources/META-INF/spring.factories 中配置实现原理

    SpringApplication 初始化时通过 SpringFactoriesLoader 获取到配置在 META-INF/spring.factories 文件中的 ApplicationContextInitializer 的所有实现类.

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    	......
        // 设置系统初始化器
    	setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    	......
    }
    
    // 获取工厂实例对象
    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
    	return getSpringFactoriesInstances(type, new Class<?>[] {});
    }
    // 获取工厂实例对象
    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
        // 获取类加载器
    	ClassLoader classLoader = getClassLoader();
    	// 使用名称并确保唯一以防止重复
    	Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        // 创建工厂实例对象
    	List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
        // 对工厂实例对象列表进行排序
    	AnnotationAwareOrderComparator.sort(instances);
    	return instances;
    }
    
    // 创建工厂实例对象
    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;
    }
    

    在 run 方法中回调 ApplicationContextInitializer 接口函数

    public ConfigurableApplicationContext run(String... args) {
    	......
    	// 准备上下文环境注入系统初始化信息 
    	prepareContext(context, environment, listeners, applicationArguments, printedBanner);
    	......
    }
    
    private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
    		SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
    	......
        // 应用初始化器   
    	applyInitializers(context);
        ......
    }
    
    protected void applyInitializers(ConfigurableApplicationContext context) {
    	for (ApplicationContextInitializer initializer : getInitializers()) {
            // 判断子类是否是 ConfigurableApplicationContext 类型
    		Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
    				ApplicationContextInitializer.class);
    		Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
            // 回调 ApplicationContextInitializer接口的 initialize 方法
    		initializer.initialize(context);
    	}
    }
    

    获取初始化器列表

    // 获取在 SpringApplication 构造函数中设置的初始化器列表
    public Set<ApplicationContextInitializer<?>> getInitializers() {
    	return asUnmodifiableOrderedSet(this.initializers);
    }
    // 对初始化器列表进行排序
    private static <E> Set<E> asUnmodifiableOrderedSet(Collection<E> elements) {
    	List<E> list = new ArrayList<>(elements);
    	list.sort(AnnotationAwareOrderComparator.INSTANCE);
    	return new LinkedHashSet<>(list);
    }
    

    5.2 在 main 函数配置实现原理

    在之前我们知道 SpringApplication 初始化之后,就已经把 META-INF/spring.factories 中配置的初始化实现类添加到 initializers 列表中了,然后通过 addInitializers 方法,添加自定义的实现类:

    public static void main(String[] args) {
        // SpringApplication.run(SpringbootApplication.class, args);
        SpringApplication springApplication = new SpringApplication(SpringbootApplication.class);
        springApplication.addInitializers(new ThirdInitializer());
        springApplication.run();
    }
    
    public void addInitializers(ApplicationContextInitializer<?>... initializers) {
    	this.initializers.addAll(Arrays.asList(initializers));
    }
    

    5.3 在配置文件中配置实现原理

    在配置文件中配置方式,主要通过内置的 DelegatingApplicationContextInitializer 实现的,它实现了 Order 方法,所以优先级最高。:

    private int order = 0;	
    
    @Override
    public int getOrder() {
    	return this.order;
    }
    

    然后我们看下它的 initialize方法实现:

    @Override
    public void initialize(ConfigurableApplicationContext context) {
        // 获取上下文环境变量
    	ConfigurableEnvironment environment = context.getEnvironment();
        // 从上下文环境变量中获取指定初始化类列表
    	List<Class<?>> initializerClasses = getInitializerClasses(environment);
    	if (!initializerClasses.isEmpty()) {
            // 应用初始化器
    		applyInitializerClasses(context, initializerClasses);
    	}
    }
    

    从上下文环境变量获取指定的属性名,并实例化对象

    private static final String PROPERTY_NAME = "context.initializer.classes";
    
    private List<Class<?>> getInitializerClasses(ConfigurableEnvironment env) {
        // 从上下文环境变量获取指定的属性名
    	String classNames = env.getProperty(PROPERTY_NAME);
    	List<Class<?>> classes = new ArrayList<>();
    	if (StringUtils.hasLength(classNames)) {
            // 将逗号分割的属性值逐个取出
    		for (String className : StringUtils.tokenizeToStringArray(classNames, ",")) {
    			// 实例化对象并添加到列表中
                classes.add(getInitializerClass(className));
    		}
    	}
    	return classes;
    }
    
    private Class<?> getInitializerClass(String className) throws LinkageError {
    	try {
    		Class<?> initializerClass = ClassUtils.forName(className, ClassUtils.getDefaultClassLoader());
    		Assert.isAssignable(ApplicationContextInitializer.class, initializerClass);
    		return initializerClass;
    	}
    	catch (ClassNotFoundException ex) {
    		throw new ApplicationContextException("Failed to load context initializer class [" + className + "]", ex);
    	}
    }
    
  • 相关阅读:
    547. Friend Circles
    399. Evaluate Division
    684. Redundant Connection
    327. Count of Range Sum
    LeetCode 130 被围绕的区域
    LeetCode 696 计数二进制子串
    LeetCode 116 填充每个节点的下一个右侧节点
    LeetCode 101 对称二叉树
    LeetCode 111 二叉树最小深度
    LeetCode 59 螺旋矩阵II
  • 原文地址:https://www.cnblogs.com/markLogZhu/p/12497258.html
Copyright © 2011-2022 走看看