Spring本质上就是一个管理程序应用的容器,而spring的一个核心功能就是自动装配,也就是在程序启动时就自动将应用所需的所有bean自动扫描、配置和装入到容器中去,方便程序的使用。
SpringBoot 定义了一套接口规范,这套规范规定:SpringBoot 应用在启动时会扫描外部引用 jar 包中的META-INF/spring.factories文件,将文件中默认配置信息加载到 Spring 容器(此处涉及到 JVM 类加载机制与 Spring 的容器知识),然后我们就可以执行类中定义的各种操作了。对于外部 jar 来说,只需要按照 SpringBoot 定义的标准,也就是对应的starter,就能将自己的功能放入Spring容器中了。
当然,如果要修改第三方类库bean的配置的话,我们在application.yaml或application.properties中编写自定义的配置就可以了,spring在配置bean的时候会优先使用yaml或properties中的配置。
在我们应用的启动类中都要加上的一个注解 @SpringBootApplication ,这个复合注解就完成了应用中所有需要的bean的自动装配。@SpringBootApplication 包含了三个主要的注解,也就是
● @SpringBootConfiguration:标注这是一个SpringBoot应用级的配置类,允许在上下文中注册额外的 bean 或导入其他配置类
● @EnableAutoConfiguration:启用 SpringBoot 的自动配置机制
● @ComponentScan: 扫描被@Component (@Service,@Controller…等)注解的 bean,注解默认会扫描启动类所在的包下所有的类 ,可以使用exclude自定义不扫描某些 bean
其中**@EnableAutoConfiguration 是实现自动装配的核心注解类**,主要通过注解内的@AutoConfigurationPackage 和所导入的 AutoConfigurationImportSelector 类来实现。其中 @AutoConfigurationPackage 的作用是将主程序类所在包下的所有bean都注册到容器中,AutoConfigurationImportSelector 的作用是选择要注册到容器中的bean。
如何选择哪些组件是要加入容器中的?
那就要看AutoConfigurationImportSelector是怎么做的了。
AutoConfigurationImportSelector 的类依赖链大致是这样的:
AutoConfigurationImportSelector -> DeferredImportSelector -> ImportSelector
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
}
public interface DeferredImportSelector extends ImportSelector {
}
public interface ImportSelector {
String[] selectImports(AnnotationMetadata var1);
}
AutoConfigurationImportSelector实现了 ImportSelector 中的 selectImports() 方法:
private static final String[] NO_IMPORTS = new String[0];
public String[] selectImports(AnnotationMetadata annotationMetadata) {
// <1>.判断自动装配开关是否打开
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
//<2>.获取所有需要装配的bean
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
需要重点关注的是 selectImport 方法中 获取所有需要装配组件 getConfigurationEntry() 方法:
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
//(1)获取元注解中的所有属性
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//(2)获取元注解中所有属性的配置项,这些作为候选配置项
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
//(3)去除重复的配置项
configurations = removeDuplicates(configurations);
//(4)去除用户自定义的需要去除的配置项
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
1. 获取元注解中的所有属性
AnnotationAttributes attributes = getAttributes(annotationMetadata);
这一步是获取元注解中的所有属性被赋予的配置项,也就是我们在 @EnableAutoConfiguration 填充的参数。
2. 获取元注解中所有属性的配置项,这些作为候选配置项
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
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;
}
Spring 会去加载所有 Spring Boot Starter 下的META-INF/spring.factories 文件中的类,这个文件中定义了第三方jar包加载的所有所需配置类。
比如这是spring-boot-starter-mybatis的META-INF/spring.factories:
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
是所有的配置项都会加载吗?
不是的,Spring中有许多的条件注解,比如@ConditionalOnClass()、@ConditionalOnMissingBean、@ConditionalOnMissingBean等,某个配置类的bean需要满足某种条件才会进行加载。比如@ConditionalOnMissingBean会限制某个bean只会在没有同类型的bean的时候才会进行装载,保证容器中只有一个同类型的bean。
下面两个就不用多说了:
3. 去除重复的配置项
configurations = removeDuplicates(configurations);
4. 去除用户自定义的需要去除的配置项
Set exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
参考资料:
淘宝一面:“说一下 Spring Boot 自动装配原理呗?