概述

  • 类名:ApplicationContextInitializer
  • 介绍:Spring容器刷新之前执行的一个回调函数
  • 作用:向SpringBoot容器中注册属性
  • 使用:继承接口自定义实现
// 可以指定@Order(XX)
public class MyInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
        // TODO:初始化逻辑实现
    }
}

实现方式

  • 都要实现ApplicationContextInitializer接口;@Order值越小越先执行;application.properties中定义的优先于其它方式。
  • 实现方式一:
    • 实现ApplicationContextInitializer接口。
    • SpringApplication类初始化后设置进去。
@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
         SpringApplication application = new SpringApplication(MyApplication.class);
         application.addInitializers(new MyInitializer());
         application.run(args);
    }
}
  • 实现方式二:(推荐)
    • 实现ApplicationContextInitializer接口。
    • spring.factories内填写接口实现。
    • key值为org.springframework.contet.ApplicationContextInitializer。
org.springframework.context.ApplicationContextInitializer=com.wzl.springboot.initializer.MyInitializer
  • 实现方式三:
    • 实现ApplicationContextInitializer接口。
    • application.properties内填写接口实现。
    • key值为context.initializer.classes
context.initializer.classes=com.wzl.springboot.initializer.MyInitializer

SpringFactoriesLoader

  • 用途:SpringFactoriesLoader类的主要作用是通过类路径下的META-INF/spring.factories文件获取工厂类接口的实现类,初始化并保存在缓存中,以供Springboot启动过程中各个阶段的调用。
  • 要点:
    1. 框架内部使用通用工厂加载机制
    2. 从classpath下多个jar包特定的位置读取文件并初始化类
    3. 文件内容必须是kv形式,即properties类型
    4. key是全限定名(抽象类|接口)、value是实现,多个实现用逗号分隔
  • 作用:
    1. 从类路径jar包中读取特定文件实现扩展类的载入
    2. loadFactories流程:
      1. 查找缓存
      2. 读取指定资源文件
      3. 构造properties对象
      4. 获取指定key对应的value值
      5. 逗号分割value
      6. 保存结果到缓存
      7. 依次实例化结果对象
      8. 对结果对象排序
      9. 返回结果
        loadFactories流程
  • spring.factories:设置目的是帮助spring-boot项目包以外的bean(即在pom文件中添加依赖中的bean)注册到spring-boot项目的spring容器中。由于@ComponentScan注解只能扫描spring-boot项目包内的bean并注册到spring容器中,因此需要@EnableAutoConfiguration注解来注册项目包外的bean。而spring.factories文件,则是用来记录项目包外需要注册的bean类名。

成员变量

// 指定spring.factories的文件路径
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
//  使用基于common-logging的运行方式记录日志
private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);
// MultiValueMap<String, String>> 表示这个ClassLoader加载器加载的类的集合
private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>();

loadFactories()

  • 用途:使用给定的类加载器,从FACTORIES_RESOURCE_LOCATION加载并实例化给定类型的工厂实现。
    • 在返回工厂之前,都会通过AnnotationAwareOrderComparator这个类来进行排序。
    • 如果需要自定义实例化策略,要使用loadFactoryNames()去获取所有注册的工厂名称。
// factoryType代表工厂的接口或抽象类,classLoader是默认为空的类加载器
public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {
    Assert.notNull(factoryType, "'factoryType' must not be null");
    // 类加载器
    ClassLoader classLoaderToUse = classLoader;
    if (classLoaderToUse == null) {
        // 如果为空则用系统默认类加载器
        classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
    }
    // 获取所有工厂实现类的名称集合
    List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);
    if (logger.isTraceEnabled()) {
        logger.trace("Loaded [" + factoryType.getName() + "] names: " + factoryImplementationNames);
    }
    List<T> result = new ArrayList<>(factoryImplementationNames.size());
    for (String factoryImplementationName : factoryImplementationNames) {
        // 实例化工厂实现类,然后添加进result集合中
        result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));
    }
    // 通过AnnotationAwareOrderComparator#sort方法对工厂名称进行排序
    AnnotationAwareOrderComparator.sort(result);
    return result;
}

loadFactoryNames()

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
    // 获取到factoryType工厂类型
    String factoryTypeName = factoryType.getName();
    // 加载SpringFactories,如果没有则返回一个空集合
    return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}

loadSpringFactories()

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
    // 从缓存中获取已经加载过的SpringFactories
    MultiValueMap<String, String> result = cache.get(classLoader);
    if (result != null) {
        return result;
    }
    try {
        // / 通过类加载器读取类路径下的spring.factories文件,然后封装成URL存储于Enumeration中
        Enumeration<URL> urls = (classLoader != null ?
                classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
        result = new LinkedMultiValueMap<>();
        // 遍历urls,再将url封装成UrlResource对象
        while (urls.hasMoreElements()) {
            URL url = urls.nextElement();
            UrlResource resource = new UrlResource(url);
            /**
                *  通过PropertiesLoaderUtils属性加载器去加载spring.factories中的value值。
                *  这里的Properties是继承了HashTable的一个属性,key和value就对应着spring.factories文件里的key和value。
                *  在PropertiesLoaderUtils中,底层是通过IO流读取的文件数据,这里就不细说了。
                */ 
            Properties properties = PropertiesLoaderUtils.loadProperties(resource);
            for (Map.Entry<?, ?> entry : properties.entrySet()) {
                String factoryTypeName = ((String) entry.getKey()).trim();
                // 遍历获取工厂实现类名称
                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);
    }
}

instantiateFactory()

// factoryImplementationName是工厂实现类的名称
private static <T> T instantiateFactory(String factoryImplementationName, Class<T> factoryType, ClassLoader classLoader) {
    try {
        // 通过classUtils工具类获取工厂实现类的Class对象
        Class<?> factoryImplementationClass = ClassUtils.forName(factoryImplementationName, classLoader);
        // 异常处理
        if (!factoryType.isAssignableFrom(factoryImplementationClass)) {
            throw new IllegalArgumentException(
                    "Class [" + factoryImplementationName + "] is not assignable to factory type [" + factoryType.getName() + "]");
        }
        // 通过反射工具创建工厂类实例对象
        return (T) ReflectionUtils.accessibleConstructor(factoryImplementationClass).newInstance();
    }
    // 异常处理
    catch (Throwable ex) {
        throw new IllegalArgumentException(
            "Unable to instantiate factory class [" + factoryImplementationName + "] for factory type [" + factoryType.getName() + "]",ex);
    }
}

系统初始化器

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

applyInitializers()

// 注册初始化器
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.");
        // 调用初始化器的initialize方法
        initializer.initialize(context);
    }
}

getInitializerClass()

  • SpringBoot在加载系统初始化器时会先加载 DelegatingApplicationContextInitializer类,因为它的order是0。而它所做的事情就是去加载context.initializer.classes下面的实现类。
    • 因此application.properties中定义的优先于其它方式。
private List<Class<?>> getInitializerClasses(ConfigurableEnvironment env) {
String classNames = env.getProperty("context.initializer.classes");
List<Class<?>> classes = new ArrayList();
if (StringUtils.hasLength(classNames)) {
    String[] var4 = StringUtils.tokenizeToStringArray(classNames, ",");
    int var5 = var4.length;
    for(int var6 = 0; var6 < var5; ++var6) {
    String className = var4[var6];
    classes.add(this.getInitializerClass(className));
    }
}
return classes;
}