• 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);
    	}
    }
    
  • 相关阅读:
    SQL语句中case函数
    动态获取数据库表中的字段名
    Java实现最基本的集中排序
    服务器上产看报错的日志的方法
    数据结构顺序表Java实现
    JavaScript创建对象
    JavaScript的基础语法
    javascript介绍
    Java小项目迷你图书管理系统
    CDN——到底用还是不用?
  • 原文地址:https://www.cnblogs.com/markLogZhu/p/12497258.html
Copyright © 2020-2023  润新知