在分析源码之前,我们先了解下自动装配的理论。没有理论的支撑,我们很难了解到源码的精华。
Spring Boot通过起步依赖为项目的依赖管理提供帮助。起步依赖其实就是特殊的Maven依 赖和Gradle依赖,利用了传递依赖解析,把常用库聚合在一起,组成了几个为特定功能而定制 的依赖。
众多的 起步依赖 均配置在 spring-boot-starter-parent 中:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
复制代码
Spring Boot的 自动配置 是应用程序启动时的过程,考虑了众多因素,才决定Spring配置应该用哪个,不该用哪个。
下面这些情况都是Spring Boot的自动配置要考虑的。
JdbcTemplate 是不是在Classpath里?如果是,并且有 DataSource 的Bean,则自动配置一个 JdbcTemplate 的Bean。 Spring Security 是不是在Classpath里?如果是,则进行一个非常基本的Web安全设置。 每当应用程序启动的时候,Spring Boot的自动配置都要做将近200个这样的决定。所有这些自动配置就是为了尽量不让我们自己写配置。
下面我们具体分析下 JdbcTemplate 和 Spring Security 自动装配的过程:
Spring Boot的 DataSourceAutoConfiguration 中定义的 JdbcTemplate Bean就是一个非常简单的例子,演示了 @ConditionalOnMissingBean 如何工作:
@Bean
@ConditionalOnMissingBean(JdbcOperations.class)
public JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(this.dataSource);
}
复制代码
jdbcTemplate() 方法上添加了 @Bean 注解,在需要时可以配置出一个 JdbcTemplate Bean。
但它上面还加了 @ConditionalOnMissingBean 注解,要求当前不存在 JdbcOperations 类型的Bean时才生效。
如果当前已经有一个 JdbcOperations Bean了,条件即不满足,不会执行 jdbcTemplate() 方法。
Spring Boot自动配置的安全配置时,最重要的一个类是 SpringBootWebSecurityConfiguration 。
以下是其中的一个代码片段:
@Configuration
@ConditionalOnClass(WebSecurityConfigurerAdapter.class)
@ConditionalOnMissingBean(WebSecurityConfigurerAdapter.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
public class SpringBootWebSecurityConfiguration {
@Configuration
@Order(SecurityProperties.BASIC_AUTH_ORDER)
static class DefaultConfigurerAdapter extends WebSecurityConfigurerAdapter {
}
}
复制代码
首先我们分析下,这些注解。
@ConditionalOnClass 的作用为项目中只有引入security相关的包,才会构建这个Bean。 @ConditionalOnMissingBean 说明当容器中没有 WebSecurityConfigurerAdapter 实例时,将采用默认配置。 @ConditionalOnWebApplication 说明这必须是个Web应用,并且类型为 servlet 。 以security为例,我们自定义配置类,需要继承抽象类 WebSecurityConfigurerAdapter ,并注入到容器即可
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
}
复制代码
我们测试一下,看是否已经注入到容器
测试类:
public class MainTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(SecurityConfig.class);
System.out.println(applicationContext.getBean(WebSecurityConfigurerAdapter.class));
}
}
复制代码
结果证明已经注入到容器中:
springboot版本:2.3.1.RELEASE
spring版本:5.2.7.RELEASE
通过上面理论的描述,接下来我们分析Springboot相关源码,看springboot到底怎么实现的自动装配?
我们先看一张图:
@SpringBootApplication :这是springboot最核心的注解,当然也是组合注解 @EnableAutoConfiguration 是自动装配的总开关。我们将以该注解入手,增强对自动配置的理解。 @Import(AutoConfigurationImportSelector.class) 导入自动配置的 ImportSelectot 类。 AutoConfigurationImportSelector 导入需要自动装配的类或Bean。 getCandidateConfigurations 获取所有组件的配置类。 AutoConfigurationMetadataLoader.loadMetadata 加载所有自动装配组件配置类的条件( @Conditional 过滤条件)。 filter.match(candidates, this.autoConfigurationMetadata) 对各组件中全部配置类根据 @Conditional 进行条件过滤。 META-INF/spring.factories 存放该组件全部配置类的全路径名。
META-INF/spring-autoconfigure-metadata.properties 存储该组件装载配置类时的全部过滤条件。
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
// 导入selector,识别各组件中的AutoConfigutaion类并装载到容器中
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
// 自动装配开关,默认true,可在application.properties中设置
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
// 不需要装载的bean
Class<?>[] exclude() default {};
// 不需要装载的bean
String[] excludeName() default {};
}
复制代码
AutoConfigurationImportSelector 该类实现 ImportSelector 接口,最重要的是实现 selectImports 方法,该方法的起到的作用是,根据配置文件( spring.factories ),将需要注入到容器的bean注入到容器。
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
// 判断自动装配开关是否打开
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
// 获取所有需要装配的bean
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
复制代码
首先我们看下,怎样判断自动装配开关的:
protected boolean isEnabled(AnnotationMetadata metadata) {
// 判断当前实例的class
if (getClass() == AutoConfigurationImportSelector.class) {
// 返回 spring.boot.enableautoconfiguration 的值,如果为null,返回true
// spring.boot.enableautoconfiguration 可在配置文件中配置,不配则为null
return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true);
}
return true;
}
复制代码
接下来,我们看如何获取需要装配的bean:
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
// 检查自动装配开关
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
// 获取EnableAutoConfiguration中的参数,exclude()/excludeName()
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 获取需要自动装配的所有配置类,读取META-INF/spring.factories
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 去重,List转Set再转List
configurations = removeDuplicates(configurations);
// 从EnableAutoConfiguration的exclude/excludeName属性中获取排除项
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
// 检查需要排除的类是否在configurations中,不在报错
checkExcludedClasses(configurations, exclusions);
// 从configurations去除exclusions
configurations.removeAll(exclusions);
// 对configurations进行过滤,剔除掉@Conditional条件不成立的配置类
configurations = getConfigurationClassFilter().filter(configurations);
// 把AutoConfigurationImportEvent绑定在所有AutoConfigurationImportListener子类实例上
fireAutoConfigurationImportEvents(configurations, exclusions);
// 返回(configurations, exclusions)组
return new AutoConfigurationEntry(configurations, exclusions);
}
复制代码
可见 selectImports() 是 AutoConfigurationImportSelector 的 核心方法
该方法的功能主要是以下三点:
META-INF/spring.factories 中 EnableAutoConfiguration 所对应的 Configuration 类列表 @EnableAutoConfiguration 注解中的 exclude/excludeName 参数筛选一遍 ConfigurationClassFilter 筛选一遍,即不满足 @Conditional 的配置类 META-INF/spring-autoconfigure-metadata.properties :
在私有静态内部类 ConfigurationClassFilter 的构造器中初始化读取 META-INF/spring-autoconfigure-metadata.properties ,代码如下
ConfigurationClassFilter(ClassLoader classLoader, List<AutoConfigurationImportFilter> filters) {
this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(classLoader);
this.filters = filters;
}
复制代码
AutoConfigurationMetadataLoader :
final class AutoConfigurationMetadataLoader {
protected static final String PATH = "META-INF/spring-autoconfigure-metadata.properties";
private AutoConfigurationMetadataLoader() {
}
static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
return loadMetadata(classLoader, PATH);
}
......
}
复制代码
META-INF/spring.factories :
通过 getCandidateConfigurations 方法读取 META-INF/spring.factories 中配置类:
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
// 加载所有META-INF/spring.factories中的配置类
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;
}
复制代码
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
// 该方法读取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 {
// FACTORIES_RESOURCE_LOCATION : META-INF/spring.factories
Enumeration<URL> 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()) {
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);
}
}
复制代码
对于官方组件,是基于condition条件来决定对于类是否要自动装配,对于第三方组件,是采用spi机制 来实现扩展
ImportSelector 实现批量导入各组件的配置类到IOC容器中。
@Conditional 详解: juejin.im/post/5ef73e…
@Enable* 模块驱动详解: juejin.im/post/5ef73f…
ImportSelector 批量动态导入Bean详解: juejin.im/post/5ef73f…