springboot2.0自动注入文件spring.factories如何加载详解 - 简书

1.首先看下启动类:


@SpringBootApplication

public class Application {

public static void main(String[] args) {

SpringApplication.run(Application.class);

    }

}

启动类使用@SpringBootApplication注解,再看下这个注解内容


@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Inherited

@SpringBootConfiguration

@EnableAutoConfiguration

@ComponentScan(excludeFilters = {

@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),

@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

public @interface SpringBootApplication {



再进入@EnableAutoConfiguration中查看


@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Inherited

@AutoConfigurationPackage

@Import(AutoConfigurationImportSelector.class)

public @interface EnableAutoConfiguration {

发现加载了一个AutoConfigurationImportSelector.class

温馨提示:所有实现ImportSelector的类,都会在启动时被ConfigurationClassParser中的processImports进行实例化,并执行selectImports方法。

2.查看AutoConfigurationImportSelector中的selectImports


@Override

public String[]selectImports(AnnotationMetadata annotationMetadata) {

if (!isEnabled(annotationMetadata)) {

return NO_IMPORTS;

  }

AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader

.loadMetadata(this.beanClassLoader);

  AnnotationAttributes attributes = getAttributes(annotationMetadata);

  List configurations = getCandidateConfigurations(annotationMetadata,

        attributes);

  configurations = removeDuplicates(configurations);

  Set exclusions = getExclusions(annotationMetadata, attributes);

  checkExcludedClasses(configurations, exclusions);

  configurations.removeAll(exclusions);

  configurations = filter(configurations, autoConfigurationMetadata);

  fireAutoConfigurationImportEvents(configurations, exclusions);

  return StringUtils.toStringArray(configurations);

}

通过方法可知是为了选出想要加载的import类,而如何获取这些类呢?其实是通过一个SpringFactoriesLoader去加载对应的spring.factories

下面展示如何和此类建立关系。

我们进入selectImports方法中的getCandidateConfigurations


protected ListgetCandidateConfigurations(AnnotationMetadata metadata,

      AnnotationAttributes attributes) {

List 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;

}

仔细看SpringFactoriesLoader.loadFactoryNames所需的参数,里面有个getSpringFactoriesLoaderFactoryClass()。下面看下这个方法的返回值是什么?

    protected Class<?> getSpringFactoriesLoaderFactoryClass() {
        return EnableAutoConfiguration.class; //返回的是一个接口
    }

可以看出返回的是一个EnableAutoConfiguration.class,接下来SpringFactoriesLoader会根据这个interface所有spring.factoriesEnableAutoConfiguration.class所对应的values,并返回。

3. SpringFactoriesLoader如何加载

此类中只有三个方法,我们使用了其中二个

  • loadFactoryNames

public static ListloadFactoryNames(Class factoryClass, @Nullable ClassLoader classLoader) {

String factoryClassName = factoryClass.getName();

  return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());

}

  • loadSpringFactories

private static Map>loadSpringFactories(@Nullable ClassLoader classLoader) {

MultiValueMap result =cache.get(classLoader);

  if (result !=null) {

return result;

  }

try {

Enumeration urls = (classLoader !=null ?

classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :

ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));

      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 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);

  }

}

classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :就是去加载对应的文件。


Original url: Access
Created at: 2019-10-31 11:17:44
Category: default
Tags: none

请先后发表评论
  • 最新评论
  • 总共0条评论