在Springboot中,@Configuration注解是最重要的注解之一,相当于Spring中的xml,关于Springboot源码中是如何解析@Configuration注解的。是我们这篇文章后面要探讨的内容。
在上篇文章中我们说了,通过Application类上面的@SpringBootApplication注解去解析注册BeanDefinition,如下代码ConfigurationClassParser类的doProcessConfigurationClass方法中.
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) { if (ConfigurationClassUtils.checkConfigurationClassCandidate( holder.getBeanDefinition(), this.metadataReaderFactory)) { parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName()); } }
上面代码中,在解析了scannedBeanDefinitions后,就会进行parse方法处理,其中就有处理各种类型bean的逻辑,包括@Configuration。
一、是怎么执行parse方法的
我们看看条件判断,checkConfigurationClassCandidate方法中,
第一个判断是否通过的条件就是:
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; } }
如果是AnnotatedBeanDefinition类型或者AbstractBeanDefinition类型,则继续向下走,否则直接返回验证不通过。AnnotatedBeanDefinition类型有个子类是ScannedGenericBeanDefinition,就是通过扫描到的bean的类的类型。
第二个判断的条件:
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; }
第一个if语句的条件是,是否@Configuration注解的类。
第二个if语句的条件是,1.不是接口?2.类上面有在下面四个类型之中的注解
candidateIndicators.add(Component.class.getName()); candidateIndicators.add(ComponentScan.class.getName()); candidateIndicators.add(Import.class.getName()); candidateIndicators.add(ImportResource.class.getName());
3.类中方法上有@Bean注解,满足任意一个即可。
执行parse方法
如果上面两个大条件通过,则可以执行parse方法。执行的parse方法最后也是调用了doProcessConfigurationClass方法。
// Process any @Import annotations processImports(configClass, sourceClass, getImports(sourceClass), true); // Process any @ImportResource annotations AnnotationAttributes importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class); if (importResource != null) { 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); } } // Process individual @Bean methods 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 != null && !superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) { this.knownSuperclasses.put(superclass, configClass); // Superclass found, return its annotation metadata and recurse return sourceClass.getSuperClass(); } }在doProcessConfigurationClass方法后面会针对不同的注解,执行不同的操作。包活@Import,@ImportResource,@Bean,superClass等。