Spring中對Configuration類的解析是通過ConfigurationClassPostProcessor進(jìn)行的掉缺,這個類是BeanFactoryPostProcessor的實(shí)現(xiàn)翼抠,在容器刷新方法中invokeBeanFactoryPostProcessors(beanFactory)這個方法調(diào)用所有的BeanFactoryPostProcessor店煞,同時(shí)也就啟動了Configuration類解析的苞慢。
解析的總體過程:
1诵原、從Bean工廠找出所有Configuratio類加入configCandidates列表中,所謂Configuratio類就是被@Configuration注解的類或者包含@Bean、@Component绍赛、@ComponentScan蔓纠、@Import、@ImportResource注解的類吗蚌。
2腿倚、根據(jù)@Order對configCandidates列表進(jìn)行排序
3、遍歷configCandidates蚯妇,使用委托類ConfigurationClassParser解析配置項(xiàng)敷燎,包含@PropertySources注解解析、@ComponentScan注解解析箩言、@Import注解解析硬贯、@Bean注解解析。
4陨收、遍歷configCandidates饭豹,使用委托類ConfigurationClassBeanDefinitionReader注冊解析好的BeanDefinition
具體配置項(xiàng)解析過程在ConfigurationClassParser類中實(shí)現(xiàn):
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<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) {
// The config class is annotated with @ComponentScan -> perform the scan immediately
Set<BeanDefinitionHolder> 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<MethodMetadata> 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)源葫、實(shí)例化元數(shù)據(jù)(注解)掃描器ClassPathBeanDefinitionScanner
2.2)、分析出Bean名稱生成器BeanNameGenerator
2.3)砖瞧、分析出代理模型ScopedProxyMode
2.4)息堂、分析出resourcePattern,默認(rèn)值"*/.class"
2.5)块促、分析出掃描包含的目錄includeFilters荣堰、排除的目錄excludeFilters,生成過濾規(guī)則
2.6)竭翠、分析出加載類型振坚,延遲或非延遲
2.7)、將上述屬性設(shè)置到ClassPathBeanDefinitionScanner
2.8)斋扰、分析出掃描的包路徑數(shù)組basePackages渡八,
2.9)啃洋、使用ClassPathBeanDefinitionScanner掃描basePackages包中符合條件的Bean注冊到容器,然后檢查Bean是否為ConfigurationClass屎鳍,如果是則遞歸解析宏娄。掃描過程:
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
//創(chuàng)建一個集合,存放掃描到BeanDefinition
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
//遍歷掃描所有給定的包
for (String basePackage : basePackages) {
//調(diào)用父類ClassPathScanningCandidateComponentProvider的方法掃描給定類路徑逮壁,獲取符合條件的BeanDefinition
Set<BeanDefinition> 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)是調(diào)用父類ClassPathScanningCandidateComponentProvider中的方法來獲取符合條件的BeanDefinition窥淆。這個類在初始化的時(shí)候卖宠,會注冊一些默認(rèn)的過濾規(guī)則,與includeFilters和excludeFilters協(xié)調(diào)工作來過濾候選BeanDefinition祖乳,注冊Spring默認(rèn)規(guī)則:
protected void registerDefaultFilters() {
// 向include過濾規(guī)則中添加@Component注解
this.includeFilters.add(new AnnotationTypeFilter(Component.class));
ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
try {
// 向include過濾規(guī)則添加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過濾規(guī)則添加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規(guī)則中眷昆,另外Spring中@Repository 蜒秤、@Service、@Controller亚斋、@Configuration都是被@Component注解過的組合注解作媚,所以添加了這些注解的類都會作為候選的Bean。獲取候選Bean:
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
// 創(chuàng)建存儲掃描到的類的集合
Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
try {
// 默認(rèn)的包路徑:this.resourcePattern=” **/*.class”帅刊,
// resolveBasePackage方法將包名中的”.”轉(zhuǎn)換為文件系統(tǒng)的”/”
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();
// 循環(huán)獲取到的資源文件
for (Resource resource : resources) {
if (traceEnabled) {
logger.trace("Scanning " + resource);
}
if (resource.isReadable()) {
try {
//為指定資源獲取元數(shù)據(jù)讀取器纸泡,元信息讀取器通過匯編(ASM)讀//取資源元信息
MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
//如果掃描到的類符合容器配置的過濾規(guī)則
if (isCandidateComponent(metadataReader)) {
//通過匯編(ASM)讀取資源字節(jié)碼中的Bean定義元信息
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
//設(shè)置Bean定義來源于resource
sbd.setResource(resource);
//為元數(shù)據(jù)元素設(shè)置配置資源對象
sbd.setSource(resource);
//檢查Bean是否是一個可實(shí)例化的對象
if (isCandidateComponent(sbd)) {
if (debugEnabled) {
logger.debug("Identified candidate component class: " + resource);
}
candidates.add(sbd);
}
// 省略 debug和異常處理的代碼 ... ...
return candidates;
}
2.10)、掃描完成返回beanDefinitions赖瞒,如果發(fā)現(xiàn)beanDefinitions中有Configuration類女揭,進(jìn)行遞歸随珠。
step 3 處理@Import注解
通過getImports(sourceClass)獲取Configuration類中使用Import注解的類
private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
Set<SourceClass> imports = new LinkedHashSet<SourceClass>();
Set<SourceClass> visited = new LinkedHashSet<SourceClass>();
collectImports(sourceClass, imports, visited);
return imports;
}
private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> 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可以會對里面的組合注解的解析有過疑問畅哑,其實(shí)springboot中組合注解的解析過程就是這個遞歸的過程:先遞歸找出所有的注解豌熄,然后再過濾出只有@Import注解的類,得到@Import注解的值墩莫。比如查找@SpringBootApplication注解的@Import注解數(shù)據(jù)的話津畸,首先發(fā)現(xiàn)@SpringBootApplication不是一個@Import注解蠢护,然后遞歸調(diào)用修飾了@SpringBootApplication的注解伺通,發(fā)現(xiàn)有個@EnableAutoConfiguration注解箍土,再次遞歸發(fā)現(xiàn)被@Import(EnableAutoConfigurationImportSelector.class)修飾,還有@AutoConfigurationPackage注解修飾罐监,再次遞歸@AutoConfigurationPackage注解吴藻,發(fā)現(xiàn)被@Import(AutoConfigurationPackages.Registrar.class)注解修飾,所以@SpringBootApplication注解對應(yīng)的@Import注解有2個弓柱,分別是@Import(AutoConfigurationPackages.Registrar.class)和@Import(EnableAutoConfigurationImportSelector.class)沟堡。所以遞歸的目的就是找出所有的Import類疮鲫,拿到這個Import列表進(jìn)行解析過程:
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> 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注解內(nèi)部的屬性類集合ComponentScanAnnotationParser
for (SourceClass candidate : importCandidates) {
if (candidate.isAssignable(ImportSelector.class)) {//如果這個類是個ImportSelector接口的實(shí)現(xiàn)類
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
// 實(shí)例化這個ImportSelector
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
ParserStrategyUtils.invokeAwareMethods(
selector, this.environment, this.resourceLoader, this.registry);
// 如果這個類也是DeferredImportSelector接口的實(shí)現(xiàn)類,
// 那么加入ConfigurationClassParser的deferredImportSelectors
if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
this.deferredImportSelectors.add(
new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
}else {
// 否則調(diào)用ImportSelector的selectImports方法得到需要Import的類
// 然后對這些類遞歸做@Import注解的處理
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
}
// 如果這個類是ImportBeanDefinitionRegistrar接口的實(shí)現(xiàn)類
// 設(shè)置到配置類的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 {
// 其它情況下把這個類入隊(duì)到ConfigurationClassParser的importStack(隊(duì)列)屬性中
// 然后把這個類當(dāng)成是@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的實(shí)例妇多,不排斥這些類中也包含Import伤哺,所以方法中也包含一個processConfigurationClass遞歸。
ImportSelector和ImportBeanDefinitionRegistrar是Spring中兩個擴(kuò)展接口者祖,分別通過selectImports方法和registerBeanDefinitions方法向容器注入Bean立莉。
另外ImportSelector還有一個子接口DeferredImportSelectors,這
個接口的實(shí)現(xiàn)類會等到Configuration類解析完之后在進(jìn)行再進(jìn)行processImports處理七问。
它們也是springboot加載自動配置的使用的注入方式蜓耻。
step 4 處理@Bean注解
這個處理過程很容易理解:
// 獲取被@Bean注解修飾的方法,然后添加到配置類的beanMethods屬性中
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
至此一個基于注解的Configuration類就解析完成了械巡,各個Bean類的BeanDefinition也注冊進(jìn)了容器刹淌,等待實(shí)例化。
碼字不易讥耗,轉(zhuǎn)載請保留原文連接http://www.reibang.com/p/b61809506d0b