AnthonyZero's Bolg

SpringBoot:Spring工厂加载机制

简介

Spring 工厂加载机制,即 Spring Factories Loader,核心逻辑是使用 SpringFactoriesLoader 加载由用户实现的类,并配置在约定好的META-INF/spring.factories 路径下,该机制可以为框架上下文动态的增加扩展。
该机制类似于 Java SPI,给用户提供可扩展的钩子,从而达到对框架的自定义扩展功能。
本文记录在SpringBoot中Spring工厂加载机制的运用。SpringBoot版本为2.0.5.RELEASE

SpringFactoriesLoader 是 Spring 工厂加载机制的核心底层实现类。它的主要作用是 从 META-INF/spring.factories 路径下加载指定接口的实现类。该文件可能存在于工程类路径下或者 jar 包之内,所以会存在多个该文件。

SpringBoot自动装配

Spring Boot 完成自动装配的核心之一就是工厂加载机制。我们以 Spring Boot 的自动装配为例来分析。如果要开启 Spring 的自动装配功能,@SpringBootApplication派生 @EnableAutoConfiguration 这个注解,这个注解会 Import AutoConfigurationImportSelector 这个类。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

AutoConfigurationImportSelector实现了ImportSelector接口(覆盖selectImport方法)

ImportSelector接口是至Spring中导入外部配置的核心接口,在SpringBoot的自动化配置和@EnableXXX(功能性注解)都有它的存在

public class AutoConfigurationImportSelector
        implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
        BeanFactoryAware, EnvironmentAware, Ordered {

}

public interface DeferredImportSelector extends ImportSelector

selectImport方法会在spring.factories文件中找出key为EnableAutoConfiguration对应的值。这些类都是自动化配置类:
spring.factories文件是以Java的Properties格式存在,key是接口或抽象类的全名、value是以逗号 “ , “ 分隔的实现类

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\

代码的实现,EnableAutoConfigurationImportSelector的selectImport方法:

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
        return NO_IMPORTS;
    }
    AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
            .loadMetadata(this.beanClassLoader);
    // 获取注解的属性
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    // 读取spring.factories属性文件中的数据
    List<String> configurations = getCandidateConfigurations(annotationMetadata,
            attributes);
    // 删除重复的配置类
    configurations = removeDuplicates(configurations);
    // 找到@EnableAutoConfiguration注解中定义的需要被过滤的配置类
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    checkExcludedClasses(configurations, exclusions);
    // 删除这些需要被过滤的配置类
    configurations.removeAll(exclusions);
    configurations = filter(configurations, autoConfigurationMetadata);
    fireAutoConfigurationImportEvents(configurations, exclusions);
    // 返回最终得到的自动化配置类
    return StringUtils.toStringArray(configurations);
}

// 读取spring.factories属性文件中的数据
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
            AnnotationAttributes attributes) {
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
            getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
    Assert.notEmpty(configurations,
            "No auto configuration classes found in META-INF/spring.factories. If you "
                    + "are using a custom packaging, make sure that file is correct.");
    return configurations;
}

// 返回key为EnableAutoConfiguration
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
    return EnableAutoConfiguration.class;
}

public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
    String factoryClassName = factoryClass.getName();
    return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}

// 从META-INF/spring.factories文件中加载 所有以 factoryClass 为 Key 的实现类
// 核心:FACTORIES_RESOURCE_LOCATION 就是"META-INF/spring.factories"
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
    MultiValueMap<String, String> result = cache.get(classLoader);
    if (result != null) {
        return result;
    }

    try {
        Enumeration<URL> urls = (classLoader != null ?
                classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
        //一Key多值 Map<K, List<V>>
        result = new LinkedMultiValueMap<>();
        while (urls.hasMoreElements()) {
            URL url = urls.nextElement();
            UrlResource resource = new UrlResource(url);
            Properties properties = PropertiesLoaderUtils.loadProperties(resource);
            for (Map.Entry<?, ?> entry : properties.entrySet()) {
                // 以逗号进行分割,得到List的实现类全限定名集合
                List<String> factoryClassNames = Arrays.asList(
                        StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));
                result.addAll((String) entry.getKey(), factoryClassNames);
            }
        }
        // 加入缓存
        cache.put(classLoader, result);
        return result;
    }
    catch (IOException ex) {
        throw new IllegalArgumentException("Unable to load factories from location [" +
                FACTORIES_RESOURCE_LOCATION + "]", ex);
    }
}

通过这样的机制,我们可以十分的方便的为框架提供各式各样的扩展插件,我们可以自己定义自己的组件的自动装配配置类,然后通过工厂加载机制让 Spring 进行加载并得到自动装配。

加载应用上下文初始器ApplicationContextInitializer

扩展一:利用 Spring 工厂加载机制,实例化 ApplicationContextInitializer 实现类,并排序对象集合

ApplicationContextInitializer 主要提供应用上下文未refresh之前的扩展,这时候可以对 ConfigurableApplicationContext 进行一些扩展处理等。

自定义一个类,实现 ApplicationContextInitializer ,并重写 initialize 方法:

import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;

@Order(Ordered.HIGHEST_PRECEDENCE) # 执行顺序跟本例无关
public class HelloworldApplicationContextInitializer implements ApplicationContextInitializer {

    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        System.out.println("应用上下文初始化:" + applicationContext.getId());
    }
}

启动 Spring Boot 程序

public class SpringApplicationBoostrap {

    public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        set.add(ApplicationConfiguration.class.getName());
        SpringApplication springApplication = new SpringApplication();
        springApplication.setSources(set);
        springApplication.setWebApplicationType(WebApplicationType.NONE);
        ConfigurableApplicationContext configurableApplicationContext = springApplication.run(args);
    }

    @SpringBootApplication
    public static class ApplicationConfiguration{

    }

    控制台输出:应用上下文初始化:org.springframework.context.annotation.AnnotationConfigApplicationContext@3e3047e6
}

实现

具体实现来自于 SpringApplicatio准备阶段

#来自SpringApplication构造函数
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    # 根据当前应用 ClassPath 中是否存在相关实现类来推断 Web 应用的类型
    this.webApplicationType = deduceWebApplicationType();
    # 加载应用上下文初始器
    setInitializers((Collection) getSpringFactoriesInstances(
            ApplicationContextInitializer.class));
    # 加载应用事件监听器
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    # 根据 Main 线程执行堆栈判断实际的引导类
    this.mainApplicationClass = deduceMainApplicationClass();
}

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 = Thread.currentThread().getContextClassLoader();
    // 还是回到实质:SpringFactoriesLoader的loadSpringFactories方法
    Set<String> names = new LinkedHashSet<>(
            SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
            classLoader, args, names);
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}

技术

  • 实现类: org.springframework.core.io.support.SpringFactoriesLoader
  • 配置资源: META-INF/spring.factories
  • 排序: AnnotationAwareOrderComparator#sort

加载应用事件监听器ApplicationListener

跟加载应用上下文初始器类似,都是运用了 Spring 工厂加载机制原理

自定义一个类,实现ApplicationListener接口, 并重写 onApplicationEvent 方法:

import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;

@Order(Ordered.HIGHEST_PRECEDENCE)
public class HelloWorldApplciationListener implements ApplicationListener<ContextRefreshedEvent> {

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        System.out.println("helloword:" + event.getApplicationContext().getId() +
                ", timestamp:" + event.getTimestamp());
    }
}

跟上面一样的代码启动 Spring Boot 程序

控制台输出:helloword:application, timestamp:1540534996327

它的实现原理和技术跟加载应用上下文初始器一样

# 加载应用上下文初始器
setInitializers((Collection) getSpringFactoriesInstances(
        ApplicationContextInitializer.class));
# 加载应用事件监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

来自于spring-boot-autoconfigure-2.0.5.RELEASE.jar包中:

# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
  • SpringBoot自动装配和应用上下文初始器、事件监听器他们都运用了Spring 工厂机制加载 META-INF/spring.factories,
  • 自动装配的核心在于Spring的条件装配和ImportSelector接口

总结

Spring Framework内部使用一种工厂加载机制(Factory Loading Mechanism)。这种机制使用SpringFactoriesLoader完成,
它是 Spring 动态加载实现类的一种方式,我们只需要遵守这个机制并在对应的文件中写出需要加载的接口和实例即可,或者自己使用SpringFactoriesLoader实现加载