为什么使用spring.factories进行Spring Boot自动配置而不是注释?

时间:2015-08-20 00:10:40

标签: spring-boot

文档说明:

  

开发自动配置和使用条件

     

如果您在开发共享库的公司工作,或者您在开源或商业库中工作,则可能需要开发自己的自动配置。自动配置类可以捆绑在外部jar中,仍然可以通过Spring Boot获取。

如果我有其他所有注释(甚至@AutoConfigureAfter或@AutoConfigureBefore注释),

为什么要将属性文件维护为指向带注释的类?

3 个答案:

答案 0 :(得分:14)

因为我们不会扫描世界以确定项目中存在哪些自动配置类。首先,自动配置只是一个常规@Configuration类。

找到Spring组件的方式是通过显式声明或组件扫描,但我们需要在实际启动上下文之前知道自动配置类的列表。

答案 1 :(得分:0)

启动SpringBoot应用程序时,它不会扫描jar中的所有类,因此SpringBoot启动程序应指定自动配置的类。例如,在spring-boot-2.0.4.RELEASE中,它的初始化如下:

@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
       //1. method run will call the construtor below
        SpringApplication.run(MyApplication.class, args);
    }
}

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        this.webApplicationType = deduceWebApplicationType();
        //2. find all the classes whose key is ApplicationContextInitializer in spring.factories and initialize them 
        setInitializers((Collection) getSpringFactoriesInstances(
                ApplicationContextInitializer.class));
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = deduceMainApplicationClass();
    }

...
    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
            Class<?>[] parameterTypes, Object... args) {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        // Use names and ensure unique to protect against duplicates
        Set<String> names = new LinkedHashSet<>(
                //3. use current thread classcloader to load resources in the classpath
                SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
                classLoader, args, names);
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }

//SpringFactoriesLoader.java
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
        String factoryClassName = factoryClass.getName();
        // 3.1  first find the configuration file
        return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
    }

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        ...
        try {
            Enumeration<URL> urls = (classLoader != null ?
            //  public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; 
            //4. spring.factories file is defined here
                    classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                    ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
            result = new LinkedMultiValueMap<>();
            ...
    }

答案 2 :(得分:0)

spring.factories中的所有条目均通过以下方法加载-

org.springframework.boot.autoconfigure.ImportAutoConfigurationImportSelector#loadFactoryNames

protected Collection<String> loadFactoryNames(Class<?> source) {
        return SpringFactoriesLoader.loadFactoryNames(source, getClass().getClassLoader());
    }

SpringFactoriesLoader属于spring-core库,请参见下面的屏幕截图

enter image description here