Spring中对Configuration类的解析是通过ConfigurationClassPostProcessor进行的,这个类是BeanFactoryPostProcessor的实现,在容器刷新方法中invokeBeanFactoryPostProcessors(beanFactory)这个方法调用所有的BeanFactoryPostProcessor,同时也就启动了Configuration类解析的。
解析的总体过程:
1、从Bean工厂找出所有Configuratio类加入configCandidates列表中,所谓Configuratio类就是被@Configuration注解的类或者包含@Bean、@Component、@ComponentScan、@Import、@ImportResource注解的类。
2、根据@Order对configCandidates列表进行排序
3、遍历configCandidates,使用委托类ConfigurationClassParser解析配置项,包含@PropertySources注解解析、@ComponentScan注解解析、@Import注解解析、@Bean注解解析。
4、遍历configCandidates,使用委托类ConfigurationClassBeanDefinitionReader注册解析好的BeanDefinition
具体配置项解析过程在ConfigurationClassParser类中实现:
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
// Recursively process any member (nested) classes first
processMemberClasses(configClass, sourceClass);
// 1、处理@PropertySources注解,解析属性文件
// 将解析出来的属性资源添加到environment
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
}
else {
logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
// 2、处理@ComponentScan注解,通过ComponentScanAnnotationParser解析@ComponentScan注解
Set componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
// The config class is annotated with @ComponentScan -> perform the scan immediately
Set scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// Check the set of scanned definitions for any further config classes and parse recursively if needed
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
// 检查是否是ConfigurationClass,如果是走递归
if (ConfigurationClassUtils.checkConfigurationClassCandidate(holder.getBeanDefinition(), this.metadataReaderFactory)) {
parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
}
}
}
}
//3、处理@Import注解
processImports(configClass, sourceClass, getImports(sourceClass), true);
// Process any @ImportResource annotations
// 处理@ImportResource注解:获取@ImportResource注解的locations属性,得到资源文件的地址信息。
// 然后遍历这些资源文件并把它们添加到配置类的importedResources属性中
if (sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) {
AnnotationAttributes importResource =
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
String[] resources = importResource.getStringArray("locations");
Class extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
configClass.addImportedResource(resolvedResource, readerClass);
}
}
// 4、处理@Bean注解:获取被@Bean注解修饰的方法,然后添加到配置类的beanMethods属性中
Set beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// 处理接口和父类
// Process default methods on interfaces
processInterfaces(configClass, sourceClass);
// Process superclass, if any
if (sourceClass.getMetadata().hasSuperClass()) {
String superclass = sourceClass.getMetadata().getSuperClassName();
if (!superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) {
this.knownSuperclasses.put(superclass, configClass);
// Superclass found, return its annotation metadata and recurse
return sourceClass.getSuperClass();
}
}
// No superclass -> processing is complete
return null;
}
step 1 处理@PropertySources注解
解析属性文件,将解析出来的属性资源添加到Environment中
step 2 处理@ComponentScan注解
通过ComponentScanAnnotationParser解析@ComponentScan注解,解析方法为parse:
2.1)、实例化元数据(注解)扫描器ClassPathBeanDefinitionScanner
2.2)、分析出Bean名称生成器BeanNameGenerator
2.3)、分析出代理模型ScopedProxyMode
2.4)、分析出resourcePattern,默认值"*/.class"
2.5)、分析出扫描包含的目录includeFilters、排除的目录excludeFilters,生成过滤规则
2.6)、分析出加载类型,延迟或非延迟
2.7)、将上述属性设置到ClassPathBeanDefinitionScanner
2.8)、分析出扫描的包路径数组basePackages,
2.9)、使用ClassPathBeanDefinitionScanner扫描basePackages包中符合条件的Bean注册到容器,然后检查Bean是否为ConfigurationClass,如果是则递归解析。扫描过程:
protected Set doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
//创建一个集合,存放扫描到BeanDefinition
Set beanDefinitions = new LinkedHashSet();
//遍历扫描所有给定的包
for (String basePackage : basePackages) {
//调用父类ClassPathScanningCandidateComponentProvider的方法扫描给定类路径,获取符合条件的BeanDefinition
Set candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
// 普通的BeanDefinition
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
// 注解的BeanDefinition
// 处理注解@Primary、@DependsOn等Bean注解
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
// 检查候选的,主要是检查BeanFactory中是否包含此BeanDefinition
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
其中findCandidateComponents(basePackage)是调用父类ClassPathScanningCandidateComponentProvider中的方法来获取符合条件的BeanDefinition。这个类在初始化的时候,会注册一些默认的过滤规则,与includeFilters和excludeFilters协调工作来过滤候选BeanDefinition,注册Spring默认规则:
protected void registerDefaultFilters() {
// 向include过滤规则中添加@Component注解
this.includeFilters.add(new AnnotationTypeFilter(Component.class));
ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
try {
// 向include过滤规则添加JSR-250:@ManagedBean注解
this.includeFilters.add(new AnnotationTypeFilter(
((Class extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
logger.debug("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
}
catch (ClassNotFoundException ex) {
// JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
}
try {
// 向include过滤规则添加JSR-330:@Named注解
this.includeFilters.add(new AnnotationTypeFilter(
((Class extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
logger.debug("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
}
catch (ClassNotFoundException ex) {
// JSR-330 API not available - simply skip.
}
}
可以看到使用使用Spring @Component注解类、使用JSR-250:@ManagedBean、JSR-330:@Named注解的类都在include规则中,另外Spring中@Repository 、@Service、@Controller、@Configuration都是被@Component注解过的组合注解,所以添加了这些注解的类都会作为候选的Bean。获取候选Bean:
public Set findCandidateComponents(String basePackage) {
// 创建存储扫描到的类的集合
Set candidates = new LinkedHashSet();
try {
// 默认的包路径:this.resourcePattern=” **/*.class”,
// resolveBasePackage方法将包名中的”.”转换为文件系统的”/”
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
boolean traceEnabled = logger.isTraceEnabled();
boolean debugEnabled = logger.isDebugEnabled();
// 循环获取到的资源文件
for (Resource resource : resources) {
if (traceEnabled) {
logger.trace("Scanning " + resource);
}
if (resource.isReadable()) {
try {
//为指定资源获取元数据读取器,元信息读取器通过汇编(ASM)读//取资源元信息
MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
//如果扫描到的类符合容器配置的过滤规则
if (isCandidateComponent(metadataReader)) {
//通过汇编(ASM)读取资源字节码中的Bean定义元信息
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
//设置Bean定义来源于resource
sbd.setResource(resource);
//为元数据元素设置配置资源对象
sbd.setSource(resource);
//检查Bean是否是一个可实例化的对象
if (isCandidateComponent(sbd)) {
if (debugEnabled) {
logger.debug("Identified candidate component class: " + resource);
}
candidates.add(sbd);
}
// 省略 debug和异常处理的代码 ... ...
return candidates;
}
2.10)、扫描完成返回beanDefinitions,如果发现beanDefinitions中有Configuration类,进行递归。
step 3 处理@Import注解
通过getImports(sourceClass)获取Configuration类中使用Import注解的类
private Set getImports(SourceClass sourceClass) throws IOException {
Set imports = new LinkedHashSet();
Set visited = new LinkedHashSet();
collectImports(sourceClass, imports, visited);
return imports;
}
private void collectImports(SourceClass sourceClass, Set imports, Set visited)
throws IOException {
if (visited.add(sourceClass)) {
for (SourceClass annotation : sourceClass.getAnnotations()) {
String annName = annotation.getMetadata().getClassName();
if (!annName.startsWith("java") && !annName.equals(Import.class.getName())) {
collectImports(annotation, imports, visited);
}
}
imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
}
}
collectImports方法中包含一个递归,如果你使用过Springboot可以会对里面的组合注解的解析有过疑问,其实springboot中组合注解的解析过程就是这个递归的过程:先递归找出所有的注解,然后再过滤出只有@Import注解的类,得到@Import注解的值。比如查找@SpringBootApplication注解的@Import注解数据的话,首先发现@SpringBootApplication不是一个@Import注解,然后递归调用修饰了@SpringBootApplication的注解,发现有个@EnableAutoConfiguration注解,再次递归发现被@Import(EnableAutoConfigurationImportSelector.class)修饰,还有@AutoConfigurationPackage注解修饰,再次递归@AutoConfigurationPackage注解,发现被@Import(AutoConfigurationPackages.Registrar.class)注解修饰,所以@SpringBootApplication注解对应的@Import注解有2个,分别是@Import(AutoConfigurationPackages.Registrar.class)和@Import(EnableAutoConfigurationImportSelector.class)。所以递归的目的就是找出所有的Import类,拿到这个Import列表进行解析过程:
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection importCandidates, boolean checkForCircularImports) throws IOException {
if (importCandidates.isEmpty()) {
return;
}
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
this.importStack.push(configClass);
try {
// 遍历这些@Import注解内部的属性类集合ComponentScanAnnotationParser
for (SourceClass candidate : importCandidates) {
if (candidate.isAssignable(ImportSelector.class)) {//如果这个类是个ImportSelector接口的实现类
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class> candidateClass = candidate.loadClass();
// 实例化这个ImportSelector
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
ParserStrategyUtils.invokeAwareMethods(
selector, this.environment, this.resourceLoader, this.registry);
// 如果这个类也是DeferredImportSelector接口的实现类,
// 那么加入ConfigurationClassParser的deferredImportSelectors
if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
this.deferredImportSelectors.add(
new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
}else {
// 否则调用ImportSelector的selectImports方法得到需要Import的类
// 然后对这些类递归做@Import注解的处理
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection importSourceClasses = asSourceClasses(importClassNames);
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
}
// 如果这个类是ImportBeanDefinitionRegistrar接口的实现类
// 设置到配置类的importBeanDefinitionRegistrars属性中
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
ParserStrategyUtils.invokeAwareMethods(
registrar, this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
} else {
// 其它情况下把这个类入队到ConfigurationClassParser的importStack(队列)属性中
// 然后把这个类当成是@Configuration注解修饰的类递归重头开始解析这个类
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass));
}
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configClass.getMetadata().getClassName() + "]", ex);
}
finally {
this.importStack.pop();
}
}
}
@Import可以引入普通类、Configuration类、ImportBeanDefinitionRegistrar和ImportSelector的实例,不排斥这些类中也包含Import,所以方法中也包含一个processConfigurationClass递归。
ImportSelector和ImportBeanDefinitionRegistrar是Spring中两个扩展接口,分别通过selectImports方法和registerBeanDefinitions方法向容器注入Bean。
另外ImportSelector还有一个子接口DeferredImportSelectors,这
个接口的实现类会等到Configuration类解析完之后在进行再进行processImports处理。
它们也是springboot加载自动配置的使用的注入方式。
step 4 处理@Bean注解
这个处理过程很容易理解:
// 获取被@Bean注解修饰的方法,然后添加到配置类的beanMethods属性中
Set beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
至此一个基于注解的Configuration类就解析完成了,各个Bean类的BeanDefinition也注册进了容器,等待实例化。
码字不易,转载请保留原文连接https://www.jianshu.com/p/b61809506d0b