跳至主要內容

SpringBoot 自动装配原理

T4makojavaSpringjavaSpringBoot大约 3 分钟

早期的Spring项目需要添加需要配置繁琐的xml,比如MVC、事务、数据库连接等繁琐的配置。SpringBoot的出现就无需这些繁琐的配置,因为SpringBoot基于约定大于配置的理念,在项目启动时候,将约定的配置类自动配置到IOC容器里。这些都因为SpringBoot有自动配置的特性。

Sping Boot 如何实现自动配置

Spring Boot都需要创建一个mian启动类,而启动类都含有@SpringBootApplication注解,从启动类,一步步探索源码。

@SpringBootApplication注解

Spring Boot 启动类上都有一个 @SpringBootApplication注解:

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.runyApplication.class,args);
    }
}

@EnableAutoConfiguration注解

@SpringBootApplication 里面有 @EnableAutoConfiguration 的注解:

...
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
...
)
...
public @interface SpringBootApplication {...}

AutoConfigurationImportSelector类

@EnableAutoConfiguration注解导入AutoConfigurationImportSelector类:

...
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    ...
}

selectImports()方法

AutoConfigurationImportSelector类找到 selectImports 方法,里面有getAutoConfigurationEntry方法:

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
	...
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            // 调用 getAutoConfigurationEntry() 方法
            AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
        }
    }
    ...
}

SpringFactoriesLoader.loadFactoryNames() 方法

getAutoConfigurationEntry方法通过SpringFactoriesLoader.loadFactoryNames() 扫描所有含有META-INF/spring.factoriesjar包:

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
    ...
    // getAutoConfigurationEntry() 方法
    protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            // 调用 getCandidateConfigurations() 方法
            List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
            configurations = this.removeDuplicates(configurations);
            Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
            this.checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            configurations = this.getConfigurationClassFilter().filter(configurations);
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            return new AutoConfigurationEntry(configurations, exclusions);
        }
    }
    ...
}
// AutoConfigurationImportSelector.class
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    // 调用 SpringFactoriesLoader.loadFactoryNames() 方法
    List<String> configurations = new ArrayList(SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader()));
    ImportCandidates.load(AutoConfiguration.class, this.getBeanClassLoader()).forEach(configurations::add);
    Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you are using a custom packaging, make sure that file is correct.");
    return configurations;
}
// SpringFactoriesLoader.class
// loadFactoryNames() 方法
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
    ClassLoader classLoaderToUse = classLoader;
    if (classLoader == null) {
        classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
    }

    String factoryTypeName = factoryType.getName();
    // 调用 loadSpringFactories() 方法
    return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
// SpringFactoriesLoader.class
// loadSpringFactories() 方法
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
    Map<String, List<String>> result = (Map)cache.get(classLoader);
    if (result != null) {
        return result;
    } else {
        Map<String, List<String>> result = new HashMap();

        try {
            // 扫描所有含有META-INF/spring.factories的jar包
            Enumeration<URL> urls = classLoader.getResources("META-INF/spring.factories");

            while(urls.hasMoreElements()) {
                URL url = (URL)urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                Iterator var6 = properties.entrySet().iterator();

                while(var6.hasNext()) {
                    Map.Entry<?, ?> entry = (Map.Entry)var6.next();
                    String factoryTypeName = ((String)entry.getKey()).trim();
                    String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                    String[] var10 = factoryImplementationNames;
                    int var11 = factoryImplementationNames.length;

                    for(int var12 = 0; var12 < var11; ++var12) {
                        String factoryImplementationName = var10[var12];
                        ((List)result.computeIfAbsent(factoryTypeName, (key) -> {
                            return new ArrayList();
                        })).add(factoryImplementationName.trim());
                    }
                }
            }

            result.replaceAll((factoryType, implementations) -> {
                return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
            });
            cache.put(classLoader, result);
            return result;
        } catch (IOException var14) {
            throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);
        }
    }
}

spring-boot-autoconfigure-xxx.jar项目包含META-INF/spring.factories文件,spring.factories是一个键值对的形式,扫描该文件下@EnableAutoConfiguration对应类:

总结

  • 自动配置主要由@EnableAutoConfiguration实现
  • 添加了@EnableAutoConfiguration注解,会导入AutoConfigurationImportSelector
  • 里面的selectImports方法通过SpringFactoriesLoader.loadFactoryNames()扫描所有含有META-INF/spring.factoriesjar
  • spring.factories是一个键值对的形式,扫描该文件下@EnableAutoConfiguration对应类:
  • 将对应key@EnableAutoConfiguration注解全名对应的value类全部装配到IOC容器中。

这些属性自动配置到IOC之后就无需自己手动配置bean了,Spring Boot中的约定大于配置理念,约定是将需要的配置以约定的方式添加到IOC容器中。

自动配置生效条件

那是不是spring.factories文件对应的配置都会加载到IOC容器中?

其中有几个注解: @ConditionalOnClass, @ConditionalOnMissingBean

  • @ConditionalOnClass表示在类路径中存在类才会配置该配置类。只有引入相关依赖才会自动配置该配置类。
  • @ConditionalOnMissingBean表示只有不存在对应的类的bean才会自动配置该类。

所以spring.factories里面并不是所有的bean都会装配到IOC容器中,只会按需配置对应的bean

评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v2.15.5