上一节讲了自动配置中@ComponentScan是如何将启动类所在的父目录下所有带@Component注解的类注册到beanfactory中的
springboot自动装配另一个重要的部分就是@EnableAutoConfiguration自动装配DispatcherServlet,DataSource等固定配置。下面从源码角度来分析其是如何实现的:
先来看看@EnableAutoConfiguration注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {...}
@EnableAutoConfiguration注解自动装配的关键就是其@Import的EnableAutoConfigurationImportSelector类,下面就分析该类是是如何起作用的
1、@Import注解解析
从上一节的doProcessConfigurationClass()函数接着说起,@ComponentScan注解解析完之后,就接着解析@Import标签,从这也就可以看出,EnableAutoConfigurationImportSelector类就是通过对@import注解解析从而被spring boot获取并且使用的
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
.......
// Process any @ComponentScan annotations
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
// 扫描组件,向容器中注册
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// 遍历bean,递归执行parse,查看其中是否还有用户自定义的配置类
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
if (ConfigurationClassUtils.checkConfigurationClassCandidate(
holder.getBeanDefinition(), this.metadataReaderFactory)) {
parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
}
}
}
}
// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), true);
.....
}
先看看getImports()函数,它获取了启动类下所有@Import注解import的类
private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
Set<SourceClass> imports = new LinkedHashSet<SourceClass>();
Set<SourceClass> visited = new LinkedHashSet<SourceClass>();
//获取sourceClass注解下所有Import的类
collectImports(sourceClass, imports, visited);
return imports;
}
可以看到EnableAutoConfigurationImportSelector类被获取到了
再看processImports()函数,挑主要的代码看看
.....
for (SourceClass candidate : importCandidates) {
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
//实例化selector
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
ParserStrategyUtils.invokeAwareMethods(
selector, this.environment, this.resourceLoader, this.registry);
if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
//将实例化的selector,加入到集合中备用
this.deferredImportSelectors.add(
new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
}
else {
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
}
}
........
从中可以看到EnableAutoConfigurationImportSelector类实例化的对象已经被spring boot获取备用了
2、开始执行springboot的默认自动配置逻辑
继续回到ConfigurationClassParser中的parse方法,回到该方法的最后一步:
public void parse(Set<BeanDefinitionHolder> configCandidates) {
//...
//开始执行默认配置
processDeferredImportSelectors();
}
跟进processDeferredImportSelectors()方法
private void processDeferredImportSelectors() {
//这就是上面的存储selector的集合
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;
Collections.sort(deferredImports, DEFERRED_IMPORT_COMPARATOR);
for (DeferredImportSelectorHolder deferredImport : deferredImports) {
ConfigurationClass configClass = deferredImport.getConfigurationClass();
try {
//获取配置类的类名
String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata());
//递归解析配置类
processImports(configClass, asSourceClass(configClass), asSourceClasses(imports), false);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configClass.getMetadata().getClassName() + "]", ex);
}
}
}
(1)获取配置类类名
跟一下获取配置类类名的过程,最后追到了loadFactoryNames()方法,是不是很熟悉,在之前的注册监听器的那一节,监听器也是通过这个方法从固定的spring.factories 获取类名的
public String[] selectImports(AnnotationMetadata annotationMetadata) {
.......
//获取默认的配置类
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
.......
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
//获取默认配置类
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
......
}
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
try {
Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
List<String> result = new ArrayList<String>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
String factoryClassNames = properties.getProperty(factoryClassName);
result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
}
return result;
}
}
springBoot为我们提供的所有配置类如下,大概100多个:
获取完之后,spring boot自动筛选出适合的配置类(根据该类是否存在,即相应的jar包是否引入了)
从中我们可以看到熟悉的DispatherServlet,Datasource
(2) 根据类名解析配置类
这个processImport()方法上面才见过,上述是为了添加selector,这里是用来解析配置类的
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, boolean checkForCircularImports) throws IOException {
for (SourceClass candidate : importCandidates) {
.......
else {
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
//解析配置类
processConfigurationClass(candidate.asConfigClass(configClass));
}
}
}
关于自动配置类是如何配置类,我在“SpringBoot之application.properties的加载”中已经讲过,再次用到这个doProcessConfigurationClass函数,这次是为了解析自动配置类中@Bean标签注解的方法
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
.......
// Process individual @Bean methods
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
......
}
我们以DataSourceAutoConfiguration为例,其中有一个方法
@Configuration
@Conditional(PooledDataSourceCondition.class)
@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
@Import({ DataSourceConfiguration.Tomcat.class, DataSourceConfiguration.Hikari.class,
DataSourceConfiguration.Dbcp.class, DataSourceConfiguration.Dbcp2.class,
DataSourceConfiguration.Generic.class })
@SuppressWarnings("deprecation")
protected static class PooledDataSourceConfiguration {
}
跟踪其中的@Import注解,比如DataSourceConfiguration.Tomcat.class,这中的@Bean就是自动配置DataSource的beanMethod,其中@ConfigurationProperties(prefix = “spring.datasource.tomcat”)标签还可以使用application.properties中以"spring.datasource.tomcat"为前缀的配置
@ConditionalOnClass(org.apache.tomcat.jdbc.pool.DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "org.apache.tomcat.jdbc.pool.DataSource", matchIfMissing = true)
static class Tomcat extends DataSourceConfiguration {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.tomcat")
public org.apache.tomcat.jdbc.pool.DataSource dataSource(
DataSourceProperties properties) {
org.apache.tomcat.jdbc.pool.DataSource dataSource = createDataSource(
properties, org.apache.tomcat.jdbc.pool.DataSource.class);
DatabaseDriver databaseDriver = DatabaseDriver
.fromJdbcUrl(properties.determineUrl());
String validationQuery = databaseDriver.getValidationQuery();
if (validationQuery != null) {
dataSource.setTestOnBorrow(true);
dataSource.setValidationQuery(validationQuery);
}
return dataSource;
}
}
执行完成之后,可以看到beanfatory中已经有了这些类的beanDefinition
至此,自动装配的内容已经介绍完了