Spring配置Bean有多種形式,第一種常用的就是通過(guò)XML文件配置施符,另外一種就是通過(guò)@Configuration聲明類甲馋,表明是一個(gè)配置文件,他的本質(zhì)作用和XML是相同的嗜湃,作為Bean的載體奈应。今天我們來(lái)和大家聊一下Spring對(duì)Configuraion的解析。
首先我們先解釋一些知識(shí)點(diǎn)购披,因?yàn)檫@些知識(shí)貫穿Spring初始化的始終杖挣。第一點(diǎn),BeanFactoryPostProcessor的作用刚陡。允許修改spring容器中Bean的定義惩妇,也就是修改BeanDefinition。第二點(diǎn)筐乳,BeanDefinitionRegistryPostProcessor的作用歌殃,他繼承了BeanFactoryPostProcessor,說(shuō)明他擁有BeanFactoryPostProcessor的能力蝙云,除此之外氓皱,他和提供方法來(lái)注冊(cè)新的BeanDefinition。第三點(diǎn)勃刨,Spring是在什么時(shí)候處理BeanFactoryPostProcessor呢波材?這個(gè)問(wèn)題的答案可以看一下org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors
的實(shí)現(xiàn),應(yīng)該不難看懂身隐。
上面的知識(shí)點(diǎn)都了解之后廷区,我們開始介紹我們今天的主角:ConfigurationClassPostProcessor。它實(shí)現(xiàn)了BeanDefinitionRegistryPostProcessor接口贾铝,所以他本身具有注冊(cè)Bean的功能躲因。那我們看看他的生命周期方法postProcessBeanDefinitionRegistry
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
...
processConfigBeanDefinitions(registry);
}
方法中只留下一個(gè)解析方法早敬,無(wú)疑他就是理解解析的關(guān)鍵。下面我們來(lái)著重講解
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<BeanDefinitionHolder>();
String[] candidateNames = registry.getBeanDefinitionNames();
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName); //關(guān)注點(diǎn)1
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
//關(guān)注點(diǎn)2
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// Return immediately if no @Configuration classes were found
if (configCandidates.isEmpty()) {
return;
}
// Sort by previously determined @Order value, if applicable
Collections.sort(configCandidates, new Comparator<BeanDefinitionHolder>() {
@Override
public int compare(BeanDefinitionHolder bd1, BeanDefinitionHolder bd2) {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return (i1 < i2) ? -1 : (i1 > i2) ? 1 : 0;
}
});
// Detect any custom bean name generation strategy supplied through the enclosing application context
SingletonBeanRegistry singletonRegistry = null;
if (registry instanceof SingletonBeanRegistry) {
singletonRegistry = (SingletonBeanRegistry) registry;
if (!this.localBeanNameGeneratorSet && singletonRegistry.containsSingleton(CONFIGURATION_BEAN_NAME_GENERATOR)) {
BeanNameGenerator generator = (BeanNameGenerator) singletonRegistry.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<BeanDefinitionHolder>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<ConfigurationClass>(configCandidates.size());
do {
//關(guān)注點(diǎn)3
parser.parse(candidates);
parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<ConfigurationClass>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
//關(guān)注點(diǎn)4
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
candidates.clear();
if (registry.getBeanDefinitionCount() > candidateNames.length) {
String[] newCandidateNames = registry.getBeanDefinitionNames();
Set<String> oldCandidateNames = new HashSet<String>(Arrays.asList(candidateNames));
Set<String> alreadyParsedClasses = new HashSet<String>();
for (ConfigurationClass configurationClass : alreadyParsed) {
alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
}
for (String candidateName : newCandidateNames) {
if (!oldCandidateNames.contains(candidateName)) {
BeanDefinition beanDef = registry.getBeanDefinition(candidateName);
if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory) &&
!alreadyParsedClasses.contains(beanDef.getBeanClassName())) {
candidates.add(new BeanDefinitionHolder(beanDef, candidateName));
}
}
}
candidateNames = newCandidateNames;
}
}
while (!candidates.isEmpty());
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
if (singletonRegistry != null) {
if (!singletonRegistry.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
singletonRegistry.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
}
}
if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
}
}
首先我們看下關(guān)注點(diǎn)1大脉,從BeanDefinitionRegistry中根據(jù)名稱獲取到具體的BeanDefinition.然后經(jīng)歷了兩個(gè)判斷方法ConfigurationClassUtils.isFullConfigurationClass
和ConfigurationClassUtils.isLiteConfigurationClass
他們的實(shí)現(xiàn)是根據(jù)具體的字符串和BeanDefinition中key為org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass中的value進(jìn)行比較得出的搞监,此處可以先忽略,后面會(huì)有地方繼續(xù)講到镰矿。下面我們看下關(guān)注點(diǎn)2琐驴,spring是如何判斷這個(gè)這個(gè)Bean是Configaration的,我們就得看看ConfigurationClassUtils.checkConfigurationClassCandidate
public static boolean checkConfigurationClassCandidate(BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
String className = beanDef.getBeanClassName();
if (className == null) {
return false;
}
AnnotationMetadata metadata;
if (beanDef instanceof AnnotatedBeanDefinition &&
className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) {
// Can reuse the pre-parsed metadata from the given BeanDefinition...
metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();
}
else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) {
// Check already loaded Class if present...
// since we possibly can't even load the class file for this Class.
Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();
metadata = new StandardAnnotationMetadata(beanClass, true);
}
else {
try {
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);
metadata = metadataReader.getAnnotationMetadata();
}
catch (IOException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Could not find class file for introspecting configuration annotations: " + className, ex);
}
return false;
}
}
if (isFullConfigurationCandidate(metadata)) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
}
else if (isLiteConfigurationCandidate(metadata)) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
}
else {
return false;
}
// It's a full or lite configuration candidate... Let's determine the order value, if any.
Map<String, Object> orderAttributes = metadata.getAnnotationAttributes(Order.class.getName());
if (orderAttributes != null) {
beanDef.setAttribute(ORDER_ATTRIBUTE, orderAttributes.get(AnnotationUtils.VALUE));
}
return true;
}
這個(gè)方法的前半部分就是為了得到這個(gè)Bean對(duì)應(yīng)的AnnotationMetadata秤标。隨后通過(guò)兩個(gè)判斷方法isFullConfigurationCandidate和isLiteConfigurationCandidate判斷是否需要給BeanDefination中增加對(duì)應(yīng)的鍵值對(duì)绝淡,其中鍵的定義就是就是我們關(guān)注點(diǎn)1中提到的org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass
。那么看看是具體怎么判斷的
public static boolean isFullConfigurationCandidate(AnnotationMetadata metadata) {
return metadata.isAnnotated(Configuration.class.getName());
}
如果類上有@Configuration注解說(shuō)明是一個(gè)完全(Full)的配置類苍姜。
public static boolean isLiteConfigurationCandidate(AnnotationMetadata metadata) {
// Do not consider an interface or an annotation...
if (metadata.isInterface()) {
return false;
}
// Any of the typical annotations found?
for (String indicator : candidateIndicators) {
if (metadata.isAnnotated(indicator)) {
return true;
}
}
// Finally, let's look for @Bean methods...
try {
return metadata.hasAnnotatedMethods(Bean.class.getName());
}
catch (Throwable ex) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to introspect @Bean methods on class [" + metadata.getClassName() + "]: " + ex);
}
return false;
}
}
如果是一個(gè)接口牢酵,那么不是簡(jiǎn)化(Lite)配置類,如果類上有candidateIndicators這個(gè)Set中定義的注解的話(@Component衙猪,@ComponentScan馍乙,@Import,@ImportResource)那么就是一個(gè)簡(jiǎn)化配置類垫释,如果不是上面兩種情況丝格,那么有@Bean注解修飾的方法也是簡(jiǎn)化配置類。OK棵譬,目前為止我們可以知道Spring是怎么判斷是一個(gè)配置類的显蝌,隨后將配置類放入到configCandidates這個(gè)BeanDefinitionHolder的集合中存儲(chǔ),進(jìn)行下一步的操作订咸。有了這個(gè)集合曼尊,下面我們就可以創(chuàng)建一個(gè)配置文件的解析器ConfigurationClassParser
類型的實(shí)例parser,進(jìn)行解析(parse)脏嚷。那我們看看關(guān)注點(diǎn)3骆撇,parse方法的實(shí)現(xiàn)。
public void parse(Set<BeanDefinitionHolder> configCandidates) {
this.deferredImportSelectors = new LinkedList<DeferredImportSelectorHolder>();
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Exception ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
processDeferredImportSelectors();
}
開頭創(chuàng)建了一個(gè)LinkedList存放DeferredImportSelectorHolder然眼,方法最后開始處理這個(gè)艾船。中間的過(guò)程就是具體的解析過(guò)程,我們看一下高每。parse有很多歌重載屿岂,但是最終著眼于,根據(jù)入?yún)⒕洌瑒?chuàng)建一個(gè)ConfigurationClass類型的實(shí)例爷怀,利用processConfigurationClass來(lái)處理這個(gè)ConfigurationClass類型的實(shí)例。我們繼續(xù)看
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
...
// Recursively process the configuration class and its superclass hierarchy.
SourceClass sourceClass = asSourceClass(configClass);
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);
this.configurationClasses.put(configClass, configClass);
}
第一個(gè)操作是處理?xiàng)l件注解(@Conditional)带欢,如果不滿足條件注解中的條件的定義运授,直接返回不進(jìn)行下面的操作烤惊。將ConfigurationClass configClass類型的數(shù)據(jù),轉(zhuǎn)化成 SourceClass sourceClass = asSourceClass(configClass);
類型的sourceClass吁朦,隨后doProcessConfigurationClass柒室。
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
// Recursively process any member (nested) classes first
//關(guān)注點(diǎn)a
processMemberClasses(configClass, sourceClass);
// Process any @PropertySource annotations
//關(guān)注點(diǎn)b
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");
}
}
// Process any @ComponentScan annotations
//關(guān)注點(diǎn)c
AnnotationAttributes componentScan = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ComponentScan.class);
if (componentScan != null && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
// 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 necessary
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
if (ConfigurationClassUtils.checkConfigurationClassCandidate(holder.getBeanDefinition(), this.metadataReaderFactory)) {
parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
}
}
}
// Process any @Import annotations
//關(guān)注點(diǎn)d
processImports(configClass, sourceClass, getImports(sourceClass), true);
// Process any
// 關(guān)注點(diǎn)e
@ImportResource annotations
if (sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) {
AnnotationAttributes importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
String[] resources = importResource.getAliasedStringArray("locations", ImportResource.class, sourceClass);
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
configClass.addImportedResource(resolvedResource, readerClass);
}
}
// Process individual
// 關(guān)注點(diǎn)f
@Bean methods
Set<MethodMetadata> beanMethods = sourceClass.getMetadata().getAnnotatedMethods(Bean.class.getName());
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// Process default methods on interfaces
// 關(guān)注點(diǎn) g
for (SourceClass ifc : sourceClass.getInterfaces()) {
beanMethods = ifc.getMetadata().getAnnotatedMethods(Bean.class.getName());
for (MethodMetadata methodMetadata : beanMethods) {
if (!methodMetadata.isAbstract()) {
// A default method or other concrete method on a Java 8+ interface...
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
}
}
// 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;
}
一個(gè)解析注解標(biāo)簽的大餐即將上演。首先看一下關(guān)注點(diǎn)a逗宜,解析成員類(內(nèi)部類)
private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
for (SourceClass memberClass : sourceClass.getMemberClasses()) {
if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) &&
!memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {
if (this.importStack.contains(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
this.importStack.push(configClass);
try {
processConfigurationClass(memberClass.asConfigClass(configClass));
}
finally {
this.importStack.pop();
}
}
}
}
}
找到所有的內(nèi)部類雄右,遍歷判斷內(nèi)部類是否是配置文件類,如果是調(diào)用處理配置文件的方法processConfigurationClass處理纺讲。內(nèi)部類處理完了擂仍,我們看下關(guān)注點(diǎn)b,怎么處理@PropertySources注解熬甚。如果Configration類上有@PropertySources注解的話逢渔,會(huì)調(diào)用processPropertySource方法處理
private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
String name = propertySource.getString("name");
String[] locations = propertySource.getStringArray("value");
boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");
Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required");
for (String location : locations) {
try {
String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
Resource resource = this.resourceLoader.getResource(resolvedLocation);
ResourcePropertySource rps = (StringUtils.hasText(name) ?
new ResourcePropertySource(name, resource) : new ResourcePropertySource(resource));
addPropertySource(rps);
}
catch (IllegalArgumentException ex) {
// from resolveRequiredPlaceholders
if (!ignoreResourceNotFound) {
throw ex;
}
}
catch (FileNotFoundException ex) {
// from ResourcePropertySource constructor
if (!ignoreResourceNotFound) {
throw ex;
}
}
}
}
操作很簡(jiǎn)單,根據(jù)注解中的value字段得到配置文件的路徑乡括,根據(jù)路徑得到ResourcePropertySource類型的對(duì)象肃廓,里面包含已經(jīng)加載成功的屬性文件中的數(shù)據(jù),隨后調(diào)用addPropertySource方法粟判,將配置屬性添加到Enviroment中驻襟。屬性文件處理成功后稠通,我們看下關(guān)注點(diǎn)c,他要做的就是處理@ComponentScan注解书闸,這個(gè)注解設(shè)計(jì)到的內(nèi)容較多唯鸭,后面會(huì)有文章進(jìn)行詳細(xì)的介紹碘勉,此處只是簡(jiǎn)單說(shuō)一下他所做的事情:首先得到@ComponentScan實(shí)例烹棉,然后放入componentScanParser這個(gè)解析器中滥崩,得到被掃描進(jìn)行的新的BeanDefinition,再次檢查新加入的這些Bean有沒(méi)有Configration類型的涂臣,如果有惨险,繼續(xù)調(diào)用parse方法羹幸,解析配置。此處@ComponentScan注解就解析完了辫愉,我們?cè)倏搓P(guān)注點(diǎn)d栅受,他是如何解析@Import注解的。
首先我們看看他是如何收集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"));
}
}
從配置類上得到所有的注解恭朗,遍歷每一個(gè)注解屏镊,如果這個(gè)注解類的不是以java
開頭,另外也不是@Import的話痰腮,繼續(xù)低估尋找而芥,最后將找到的Import注解中的value的值放到imports這個(gè)集合中,當(dāng)然最后返回的肯定也是imports這個(gè)集合了膀值,所以尋找的過(guò)程是遞歸尋找@Import尋找棍丐。找到具體的要導(dǎo)入的資源了误辑,我們看看他是怎么處理這些導(dǎo)入的
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, boolean checkForCircularImports) throws IOException {
if (importCandidates.isEmpty()) {
return;
}
if (checkForCircularImports && this.importStack.contains(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
this.importStack.push(configClass);
try {
for (SourceClass candidate : importCandidates) {
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
invokeAwareMethods(selector);
if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
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);
}
}
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);
invokeAwareMethods(registrar);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
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));
}
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Exception ex) {
throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" +
configClass.getMetadata().getClassName() + "]", ex);
}
finally {
this.importStack.pop();
}
}
}
其實(shí)可以清晰的看到有三種情況,第一種導(dǎo)入的類是ImportSelector類型的歌逢。利用反射實(shí)例化這個(gè)類巾钉,隨后將一些應(yīng)該注入的注入進(jìn)去,如果是DeferredImportSelector類型的秘案,那么就放到deferredImportSelectors這個(gè)集合里睛琳,在文章的開篇我們聲明了這個(gè)集合,可以到文章開頭回顧下踏烙。如果不是這種類型的师骗,就調(diào)用selectImports
方法得到需要導(dǎo)入的類,隨后繼續(xù)遞歸此方法讨惩,進(jìn)行導(dǎo)入辟癌。第二種是ImportBeanDefinitionRegistrar類型,一樣利用反射進(jìn)行初始化荐捻,隨后注入屬性黍少,隨后調(diào)用了configClass.addImportBeanDefinitionRegistrar
添加進(jìn)來(lái)了。最后一種情況導(dǎo)入的就是一個(gè)配置文件Configration類处面,直接調(diào)用processConfigurationClass方法進(jìn)行處理厂置。@Import注解就基本上說(shuō)完了。隨后我們看看關(guān)注點(diǎn)e魂角,處理@ImportResource注解昵济。這個(gè)操作也是比較簡(jiǎn)單,如果配置問(wèn)價(jià)類上有@ImportResource注解野揪,獲取其中l(wèi)ocations字段中的值访忿,通過(guò)解析占位符等得到真正的路徑,通過(guò)addImportedResource添加到configClass中斯稳。接著我們要處理的就是@Bean了海铆,看一下關(guān)注點(diǎn)f.得到所有的利用@Bean注解聲明的方法,利用addBeanMethod方法挣惰,將封裝成BeanMethod類型的對(duì)象卧斟,添加到configClass中。針對(duì)java8的默認(rèn)方法憎茂,spring也進(jìn)行了支持珍语,看一下關(guān)注點(diǎn)g,獲取配置類上的所有的接口,如果接口中的方法有@Bean注解修飾唇辨,那么也會(huì)添加到configClass中廊酣。還有一個(gè)部分,我們提到了一個(gè)延遲的ImportSelector的集合deferredImportSelectors赏枚,因?yàn)樯厦娴牧鞒讨杏刑砑訑?shù)據(jù)到這個(gè)結(jié)合中亡驰,最后我們看看他是如何處理的org.springframework.context.annotation.ConfigurationClassParser#processDeferredImportSelectors
private void processDeferredImportSelectors() {
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 (Exception ex) {
throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" +
configClass.getMetadata().getClassName() + "]", ex);
}
}
}
和正常處理的沒(méi)有太大的不同晓猛,可能就是為了延遲執(zhí)行
吧。獲取到所有要導(dǎo)入的類凡辱,然后調(diào)用processImports方法進(jìn)行真正的導(dǎo)入戒职。OK,解析的流程我們就告一段落透乾,將這個(gè)類中的所有的注解解析了一遍洪燥,有些數(shù)據(jù)也放到了configClass里面,還沒(méi)有用到乳乌。下面我們?cè)倏纯此窃倌睦镉玫降呐踉稀?匆幌?code>org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitions的關(guān)注點(diǎn)4汉操,創(chuàng)建了一個(gè)讀取器再来,使用此讀取器通過(guò)配置好的ConfigurationClass進(jìn)行加載BeanDefinition
private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass,
TrackedConditionEvaluator trackedConditionEvaluator) {
if (trackedConditionEvaluator.shouldSkip(configClass)) {
String beanName = configClass.getBeanName();
if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
this.registry.removeBeanDefinition(beanName);
}
this.importRegistry.removeImportingClassFor(configClass.getMetadata().getClassName());
return;
}
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
里面的registerBeanDefinitionForImportedConfigurationClass
、loadBeanDefinitionsForBeanMethod
磷瘤、loadBeanDefinitionsFromImportedResources
芒篷、loadBeanDefinitionsFromRegistrars
分別做的事情時(shí)將此Configuration注冊(cè)成了BeanDefinition,將配置文件里面的使用@Bean注釋的方法變成了BeanDefinition,將ImportResource進(jìn)來(lái)的配置文件加載解析采缚,從資源文件加載合適的Bean,最后一個(gè)就是調(diào)用Config類中ImportBeanDefinitionRegistrar類型的對(duì)象的registerBeanDefinitions方法针炉,實(shí)現(xiàn)他們內(nèi)部的注冊(cè)邏輯。
今天講了一大通扳抽,終于把@Configuration注解的處理講完了篡帕,希望可以幫助到需要的朋友。