zoukankan      html  css  js  c++  java
  • PDS-ApplicationContextInitializer

    1.介绍下SpringFactoriesLoader

    • 框架内部使用的通用加载机制
    • 加载并实例化类路径下(包含jar包)META-INF/spring.factories指定类型的类型;
    • 可以自定义实例化方式,只使用加载功能,返回List<String>,值为类的全限定类名;
    • META-INF/spring.factories文件的格式为Properties格式,即K/V格式。其中key一般为接口或抽象类的全限定类名,value为实现类,多个实现逗号分隔

    Spring Factories Loader

    2.SpringFactoriesLoader如何加载工厂类?

    对外暴露了两个 public方法,具体如下:

    loadFactories

    public static <T> List<T> loadFactories(Class<T> factoryClass, @Nullable ClassLoader classLoader) {
          //实际的FactoryClass为 META-INF/spring.factories文件中的某个key
            Assert.notNull(factoryClass, "'factoryClass' must not be null");
            ClassLoader classLoaderToUse = classLoader;
            if (classLoaderToUse == null) {
                classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
            }
          //根据名字和参数就能知道,获取名字;这个方法比较核心,跟进去看一下;
            List<String> factoryNames = loadFactoryNames(factoryClass, classLoaderToUse);
            if (logger.isTraceEnabled()) {
                logger.trace("Loaded [" + factoryClass.getName() + "] names: " + factoryNames);
            }
            List<T> result = new ArrayList<>(factoryNames.size());
          //for 循环反射实例化对象;
            for (String factoryName : factoryNames) {
                result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse));
            }
          //如果根据@Order注解中的值进行排序
            AnnotationAwareOrderComparator.sort(result);
            return result;
        }
    
    

    loadFactoryNames

    public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
            String factoryClassName = factoryClass.getName();
          //loadSpringFactories的返回是一个map,然后调用getOrDefault方法,具体api可以参加guava.
            return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
        }

    loadSpringFactories

        private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
            //老套路,尝试从缓存取
            MultiValueMap<String, String> result = cache.get(classLoader);
            if (result != null) {
                return result;
            }
    
    
            try {
                //通过classLoader加载对应的META-INF/spring.factories文件。
                Enumeration<URL> urls = (classLoader != null ?
                        classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                        ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
    
                result = new LinkedMultiValueMap<>();
                //迭代,放在map中。这里存在一k多v的情况,所以用的是LinkedMultiValueMap
                while (urls.hasMoreElements()) {
                    URL url = urls.nextElement();
                    UrlResource resource = new UrlResource(url);
                    //借助工具类转换为properties文件,其实就是一个k/v都为string的map
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    for (Map.Entry<?, ?> entry : properties.entrySet()) {
                        String factoryClassName = ((String) entry.getKey()).trim();
                        for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                            //result类型是LinkedMultiValueMap,允许一K多V。
                            result.add(factoryClassName, factoryName.trim());
                        }
                    }
                }
                //放到缓存中
                cache.put(classLoader, result);
                return result;
            }
            catch (IOException ex) {
                throw new IllegalArgumentException("Unable to load factories from location [" +
                        FACTORIES_RESOURCE_LOCATION + "]", ex);
            }
        }
    
    

    根据上面的调用流程可以知道,可以选择加载&实例化,对应<T> List<T> loadFactories方法;也可以选择只加载,拿到全限定类名,自己想干什么都可以,对应 List<String> loadFactoryNames

    3.系统初始化器的作用?

    • 回调接口,用于初始化 Spring Context,调用时机在reresh方法之前;
    • 通过实现该接口,可以在手动给应用上下文注入一些内容,如给Environment注入自定义属性;
    • 可以根据实现类上的 @Order注解进行排序;

    4.系统初始化器调用时机?

    org.springframework.boot.SpringApplication#run(java.lang.String...)#prepareContext方法处调用:

    5.如何自定义实现系统初始化器?

    方式一

    • 实现ApplicationContextInitializer接口
    • resources/META-INF/spring.factories内填写接口实现
    • key值为org.springframework.context.ApplicationContextlnitializer
    @Order(1)
    public class FirstInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
        @Override
        public void initialize(ConfigurableApplicationContext applicationContext) {
            ConfigurableEnvironment environment = applicationContext.getEnvironment();
            environment.setRequiredProperties("mooc");
            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");
        }
    }
    #配置
    org.springframework.context.ApplicationContextInitializer=com.mooc.sb2.initializer.FirstInitializer

    方式二

    • 实现ApplicationContextInitializer接口
    • SpringApplication类初始后设置进去
    SpringApplication application = new SpringApplication(Sb2Application.class);
    SecondInitializer initializer = new SecondInitializer();
    application.addInitializers(initializer);
    application.run(args);

    方式三

    • 实现ApplicationContextInitializer接口
    • application.properties内填写接口实现
    • key值为context.initializer.classes
    #application.properties
    context.listener.classes=com.mooc.sb2.listener.ThirdListener

    6.自定义实现系统初始化器有哪些注意事项?

    • 都要实现ApplicationContextInitializer接口
    • Order值越小越先执行
    • application.properties中定义的优先于其它方式




    网络知识搬运/梳理小工
  • 相关阅读:
    XVI Open Cup named after E.V. Pankratiev. GP of Ekaterinburg.
    2017 Multi-University Training Contest
    spring IOC快速入门,属性注入,注解开发
    hibernate注解开发,三种查询语句
    hibernate主键生成策略,一级缓存,一对多关系配置
    struts2值栈,OGNL表达式,interceptor
    Oracle基础进阶
    Oracle基础
    mysql和Oracle的简单比较
    Linux的基础命令
  • 原文地址:https://www.cnblogs.com/aibilim/p/12107349a587cdc9587c45684659d2e0.html
Copyright © 2011-2022 走看看