1.什么是 SpringBoot 自动装配?
我们现在提到自动装配的时候,一般会和Spring Boot联系在一起。但是实际上SpringFramework 早就实现了这个功能。Spring Boot 只是在其基础上,通过 SPI 的方式,做了进一步优化。
SpringBoot 定义了一套接口规范:
SpringBoot 在启动时会扫描外部引用 jar 包中的META-INF/spring.factories
文件,将文件中配置的类型信息加载到 Spring 容器(此处涉及到 JVM 类加载机制与 Spring 的容器知识),并执行类中定义的各种操作。对于外部 jar 来说,只需要按照 SpringBoot 定义的标准,就能将自己的功能装置进 SpringBoot。
没有 Spring Boot 的情况下,如果我们需要引入第三方依赖,需要手动配置,非常麻烦。但是,Spring Boot 中,我们直接引入一个 starter 即可。比如你想要在项目中使用 redis 的话,直接在项目中引入对应的 starter 即可。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
引入 starter 之后,我们通过少量注解和一些简单的配置就能使用第三方组件提供的功能了。
2.SpringBoot 是如何实现自动装配的?
依赖SpringBoot的核心注解实现 @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 {
xxxx
}
其中@EnableAutoConfiguration
是实现自动装配的重要注解,我们以这个注解入手。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
xxx
}
可以看到自动装配核心功能的实现实际是通过 AutoConfigurationImportSelector
类
那我们继续分析下AutoConfigurationImportSelector
类到底做了什么?
AutoConfigurationImportSelector
:加载自动装配类
可以看到他是实现了 ImportSelector
接口,也就实现了这个接口中的 selectImports
方法,该方法主要用于获取所有符合条件的类的全限定类名,这些类需要被加载到 IoC 容器中。
public String[] selectImports(AnnotationMetadata annotationMetadata) {
// 判断自动装配开关是否打开 默认是开启
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
// 获取获取所有需要装配的bean
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
我们在继续看getAutoConfigurationEntry
方法是如何获取装配bean的
/**
* Return the {@link AutoConfigurationEntry} based on the {@link AnnotationMetadata}
* of the importing {@link Configuration @Configuration} class.
* @param annotationMetadata the annotation metadata of the configuration class
* @return the auto-configurations that should be imported
*/
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
// 获取EnableAutoConfiguration注解中的 exclude 和 excludeName。
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 获取装配Bean
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 过滤重复bean
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
// 移除需要排除的bean
configurations.removeAll(exclusions);
// 根据AutoConfigurationImportFilter的match方法来判断是否符合OnBeanCondition,OnClassCondition,OnWebApplicationCondition 过滤bean
configurations = getConfigurationClassFilter().filter(configurations);
// 触发自动配置导入事件
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
其中getCandidateConfigurations
和 getConfigurationClassFilter().filter
方法为核心
getCandidateConfigurations
获取配置bean ,如图所示
主要调用 org.springframework.core.io.support.SpringFactoriesLoader#loadFactoryNames
再调用 org.springframework.core.io.support.SpringFactoriesLoader#loadSpringFactories
如图所示
其原理就是读取META-INF/spring.factories
文件的的URL
getConfigurationClassFilter().filter 方法用于根据AutoConfigurationImportFilter的match方法来判断是否符合OnBeanCondition,OnClassCondition,OnWebApplicationCondition 过滤bean 如图所示
最后,总结下SpringBoot自动配置的原理,主要做了以下事情:
- 从
spring.factories
配置文件中加载自动配置类,利用org.springframework.core.io.support.SpringFactoriesLoader#loadSpringFactories
方法实现 - 加载的自动配置类中排除掉
@EnableAutoConfiguration注解的exclude
属性指定的自动配置类; - 再用
AutoConfigurationImportFilter
接口去过滤自动配置类是否符合其标注注解
OnBeanCondition(如:ConditionalOnBean、ConditionalOnMissingBean)、OnClassCondition(如:ConditionalOnClass、ConditionalOnMissingClass)、OnWebApplicationCondition(如ConditionalOnWebApplication、ConditionalOnNotWebApplication)
的条件,若都符合的话则返回匹配结果; - 触发AutoConfigurationImportEvent事件
- 最后spring再将最后筛选后的自动配置类导入IOC容器中
拓展
1.条件注解
● @ConditionalOnBean
:当容器里有指定 Bean 的条件下
● @ConditionalOnMissingBean
:当容器里没有指定 Bean 的情况下
● @ConditionalOnSingleCandidate
:当指定 Bean 在容器中只有一个,或者虽然有多个但是指定首选 Bean
● @ConditionalOnClass
:当类路径下有指定类的条件下
● @ConditionalOnMissingClass
:当类路径下没有指定类的条件下
● @ConditionalOnProperty
:指定的属性是否有指定的值
● @ConditionalOnResource
:类路径是否有指定的值
● @ConditionalOnExpression
:基于 SpEL 表达式作为判断条件
● @ConditionalOnJava
:基于 Java 版本作为判断条件
● @ConditionalOnJndi
:在 JNDI 存在的条件下差在指定的位置
● @ConditionalOnNotWebApplication
:当前项目不是 Web 项目的条件下
● @ConditionalOnWebApplication
:当前项目是 Web 项 目的条件下
2.控制自动装配顺序注解
● @AutoConfigureOrder
● @AutoConfigureBefore
● @AutoConfigureAfter
● @AutoConfiguration(after = xx.class, before = xx.class)
@AutoConfigureOrder
此需要制定自定配置的顺序时,可以用 @AutoConfigureOrder
,表示绝对顺序(数字越小,优先顺序越高)。
@AutoConfigureBefore
和 @AutoConfigureAfter
控制应用配置类的相对顺序。
具体顺序如下:
- 根据类名按照字母表递增排序
- 根据
@AutoConfigureOrde
r value 值(默认:0)递增排序 - 根据
@AutoConfigureBefore
和@AutoConfigureAfter
调整排序
AutoConfigurationSorter
类是具体实现排序的逻辑