功能说明
ConfigurationClassParser
是Spring解析Java配置(@Configuration)的核心类,主要方法是org.springframework.context.annotation.ConfigurationClassParser#parse(java.util.Set<org.springframework.beans.factory.config.BeanDefinitionHolder>)
。首先需要弄懂几个类:
第一个就是ConfigurationClass
,顾名思义,这个类就是所有包含注解@Configuration
的抽象类。比如:
@Configuration
@Import(BootstrapImportSelector.class)
public class BootstrapImportSelectorConfiguration {
}
ConfigurationClassParser
的最主要的目的就是解析一个类为ConfigurationClass
,解析完成之后存放到configurationClasses
属性中,如下图所示:
如果要弄懂这个解析的过程,必须弄懂Spring的自动注入的几种方式以及ConfigurationClass
这个类的含义。
首先抽略看一下ConfigurationClass
类的定义
通常注入一个类有以下几种方式:
@Bean
@Configuration
public class AppConfig {
@Bean
public MyService myService() {
return new MyServiceImpl();
}
}
通过@Bean
的方式,此时AppConfig
整个类作为ConfigurationClass
,而myService
作为BeanMethod
对象保存到属性beanMethods
中。
@ComponentScan注解
这就会扫描对应目录下的注解,比如@Component
,当然也包括@Configuration
,此时不是作为原来ConfigurationClass
对象了,而是另一个ConfigurationClass
对象,通过importedBy
属性来记录原来的ConfigurationClass
对象。
@Configuration
@ComponentScan(basePackages = "com.acme")
public class AppConfig {
...
}
@Import注解
@Configuration
public class ConfigA {
@Bean
public A a() {
return new A();
}
}
@Configuration
@Import(ConfigA.class)
public class ConfigB {
@Bean
public B b() {
return new B();
}
}
比如上面这种情况,通过ConfigB
注入了ConfigA
,这个时候会将ConfigA
、ConfigB
都作为ConfigurationClass
来处理,其中ConfigA
对应的ConfigurationClass
对象的属性importedBy
就包含ConfigB
所对应的ConfigurationClass
对象。
当然@Import不止以上一种,还包括另外两种情形,用来注入ImportSelector
类或者ImportBeanDefinitionRegistrar
类。
比如Spring Boot中一个很重要的注解
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
// ... 省略主要部分
}
通过引入AutoConfigurationImportSelector
,在接口方法selectImports
来获取Spring Boot通过SPI机制自动注入的类名称列表。
通过ImportSelector
接口的方式引入的类比较复杂,如果selectImports
返回的类是包含@Configuration
的,跟上面的相似,也会作为ConfigurationClass
对象来处理。如果是ImportBeanDefinitionRegistrar
,比如开启AOP的注解引入的AspectJAutoProxyRegistrar
就是这样一个类型。
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
// ....
}
这时候在ImportBeanDefinitionRegistrar
引入的类会作为ConfigurationClass
中的属性importBeanDefinitionRegistrars
中的一员。
此外还有一些其他的方式 通过@ImportSource
注解引入资源,此时则是添加到ConfigurationClass
中的属性importedResources
中。
而至于PropertySources
,则是直接赋值到ConfigurationClassParser#environment
属性中。
总之,解析的最终目标无非就是填充ConfigurationClassParser#configurationClasses
属性,以及将不同形式注册的类放到ConfigurationClass
中的对应属性中,比如importedBy
记录被谁引入、beanMethods
存放@Bean
引入的、importedResources存放通过@ImportSource
注解引入的资源,而importBeanDefinitionRegistrars
则存放通过ImportBeanDefinitionRegistrar
接口引入的。
如下图所示:
主要使用场景
这个类在ConfigurationClassPostProcessor中的使用如下:
// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
// 所有需要进行解析的@Configuration类,当然还包括其他的比如@Component、@ComponentScan、@Import、@ImportResource
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
// 用于存放已经解析好的ConfigurationClass类
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
// 进行解析
parser.parse(candidates);
// 进行眼这个你
parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(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());
}
// 加载bean定义
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
candidates.clear();
if (registry.getBeanDefinitionCount() > candidateNames.length) {
String[] newCandidateNames = registry.getBeanDefinitionNames();
Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
Set<String> alreadyParsedClasses = new HashSet<>();
for (ConfigurationClass configurationClass : alreadyParsed) {
alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
}
for (String candidateName : newCandidateNames) {
if (!oldCandidateNames.contains(candidateName)) {
BeanDefinition bd = registry.getBeanDefinition(candidateName);
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
!alreadyParsedClasses.contains(bd.getBeanClassName())) {
candidates.add(new BeanDefinitionHolder(bd, candidateName));
}
}
}
candidateNames = newCandidateNames;
}
}
while (!candidates.isEmpty());
ConfigurationClassParser构造
/**
* Create a new {@link ConfigurationClassParser} instance that will be used
* to populate the set of configuration classes.
*/
public ConfigurationClassParser(MetadataReaderFactory metadataReaderFactory,
ProblemReporter problemReporter, Environment environment, ResourceLoader resourceLoader,
BeanNameGenerator componentScanBeanNameGenerator, BeanDefinitionRegistry registry) {
this.metadataReaderFactory = metadataReaderFactory;
this.problemReporter = problemReporter;
this.environment = environment;
this.resourceLoader = resourceLoader;
this.registry = registry;
this.componentScanParser = new ComponentScanAnnotationParser(
environment, resourceLoader, componentScanBeanNameGenerator, registry);
this.conditionEvaluator = new ConditionEvaluator(registry, environment, resourceLoader);
}
其中各个核心属性作用如下:
MetadataReaderFactory
:用于根据类名称获取MetadataReader,读取一个类的各种元数据,比如是否包含某个注解、所有注解的类型,这样就可以轻松的判断一个类是否是需要通过注解注入的类
ResourceLoader
:资源读取类,可以用于读取各种文件或网络流数据,在此处应该主要是用于加载classpath下面的class文件
BeanNameGenerator
:当将一个class类生成一个BeanDefinition的时候,需要一个唯一的名称,就是通过这个类来实现的
BeanDefinitionRegistry
:用于注册Bean定义
以上这个都是通过外面传入的,另外在这个类里面,自己构造了一个类对象,这个类就是ConditionEvaluator
,当一个类上面添加了的@Conditional
类型的注解的时候,就需要判断这个类是否要注册到Spring容器中,那么此时ConditionEvaluator就派上用场了。
当收集完所有的ConfigurationClass
后,就会通过ConfigurationClassParser
针对其中每一个进行解析了。
ConfigurationClassParser核心方法parse
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
// 针对带有注解的
if (bd instanceof AnnotatedBeanDefinition) {
// bean定义包含元数据信息
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
// bean定义中包括类信息
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
// bean定义中包含类名称
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
this.deferredImportSelectorHandler.process();
}
无论是带有注解的,还是bean定义包含类信息,或者类名称的,最终都是构造一个ConfigurationClass
对象,然后再进行处理的。
protected final void parse(@Nullable String className, String beanName) throws IOException {
Assert.notNull(className, "No bean class name for configuration class bean definition");
MetadataReader reader = this.metadataReaderFactory.getMetadataReader(className);
processConfigurationClass(new ConfigurationClass(reader, beanName));
}
protected final void parse(Class<?> clazz, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(clazz, beanName));
}
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(metadata, beanName));
}
- 构造
ConfigurationClass
对象
/**
* Create a new {@link ConfigurationClass} with the given name.
* @param metadataReader reader used to parse the underlying {@link Class}
* @param beanName must not be {@code null}
* @see ConfigurationClass#ConfigurationClass(Class, ConfigurationClass)
*/
public ConfigurationClass(MetadataReader metadataReader, String beanName) {
Assert.notNull(beanName, "Bean name must not be null");
this.metadata = metadataReader.getAnnotationMetadata();
this.resource = metadataReader.getResource();
this.beanName = beanName;
}
/**
* Create a new {@link ConfigurationClass} with the given name.
* @param clazz the underlying {@link Class} to represent
* @param beanName name of the {@code @Configuration} class bean
* @see ConfigurationClass#ConfigurationClass(Class, ConfigurationClass)
*/
public ConfigurationClass(Class<?> clazz, String beanName) {
Assert.notNull(beanName, "Bean name must not be null");
this.metadata = new StandardAnnotationMetadata(clazz, true);
this.resource = new DescriptiveResource(clazz.getName());
this.beanName = beanName;
}
/**
* Create a new {@link ConfigurationClass} with the given name.
* @param metadata the metadata for the underlying class to represent
* @param beanName name of the {@code @Configuration} class bean
* @see ConfigurationClass#ConfigurationClass(Class, ConfigurationClass)
*/
public ConfigurationClass(AnnotationMetadata metadata, String beanName) {
Assert.notNull(beanName, "Bean name must not be null");
this.metadata = metadata;
this.resource = new DescriptiveResource(metadata.getClassName());
this.beanName = beanName;
}
类ConfigurationClass
中的属性如下:
// 当前ConfigurationClass类对象的注解元数据
private final AnnotationMetadata metadata;
// 当前ConfigurationClass类定义的资源文件
private final Resource resource;
// 对应的Spring中的beanName
@Nullable
private String beanName;
// 被引入的那个类
private final Set<ConfigurationClass> importedBy = new LinkedHashSet<>(1);
// 定义的带有@Bean注解的方法
private final Set<BeanMethod> beanMethods = new LinkedHashSet<>();
// @ImportSource
private final Map<String, Class<? extends BeanDefinitionReader>> importedResources =
new LinkedHashMap<>();
// 通过@Import额外引入的ImportBeanDefinitionRegistrar定义的bean
private final Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> importBeanDefinitionRegistrars =
new LinkedHashMap<>();
final Set<String> skippedBeanMethods = new HashSet<>();
创建完毕之后,就会进行处理了,首先会判断一下当前类是否需要进行处理,也就是判断Conditional
这些条件是否满足环境要求,如果经过判断需要进行过滤,那么就不会继续进行解析了,立即返回。
读取缓存configurationClasses
,第一次这里通常是没有值的
然后将configClass
转为一个SourceClass
类对象,其实就是对类和元数据进行一下包装。因为该类提供了一堆有用的方法,比如加载类对loadClass
,判断继承的类或实现的接口isAssignable
,返回ConfigurationClass
对象asConfigClass
,获取成员类getMemberClasses
等等。
2. 解析ConfigurationClass
对象
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
// 此时的ConfigurationPhase时机是PARSE_CONFIGURATION,对于ConditionalOnBean是不起作用的
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
if (existingClass != null) {
if (configClass.isImported()) {
if (existingClass.isImported()) {
existingClass.mergeImportedBy(configClass);
}
// Otherwise ignore new imported config class; existing non-imported class overrides it.
return;
}
else {
// Explicit bean definition found, probably replacing an import.
// Let's remove the old one and go with the new one.
this.configurationClasses.remove(configClass);
this.knownSuperclasses.values().removeIf(configClass::equals);
}
}
// 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);
}
完成一些类的准备工作以后(BeanDefinitionHolder
->ConfigurationClass
->SourceClass
),开始真正的解析工作了。主要处理如下的注解@Component
、@PropertySources
、@ComponentScan
、@ComponentScans
、@Import
、@ImportResource
、@Bean
、以及默认接口方法、父类逻辑。
/**
* Apply processing and build a complete {@link ConfigurationClass} by reading the
* annotations, members and methods from the source class. This method can be called
* multiple times as relevant sources are discovered.
* @param configClass the configuration class being build
* @param sourceClass a source class
* @return the superclass, or {@code null} if none found or previously processed
*/
@Nullable
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
// Recursively process any member (nested) classes first
processMemberClasses(configClass, sourceClass);
}
// Process any @PropertySource annotations
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
}
else {
logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
// 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) {
// 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) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
// 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();
}
}
// No superclass -> processing is complete
return null;
}
processMemberClasses 处理成员内部类
针对所有的内部类进行遍历,查看是否属于包含有效注解(@Configuration
、Component
、ComponentScan
、Import
、ImportResource
)
/**
* Register member (nested) classes that happen to be configuration classes themselves.
*/
private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
Collection<SourceClass> memberClasses = sourceClass.getMemberClasses();
if (!memberClasses.isEmpty()) {
List<SourceClass> candidates = new ArrayList<>(memberClasses.size());
for (SourceClass memberClass : memberClasses) {
if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) &&
!memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {
candidates.add(memberClass);
}
}
针对满足要求的类进行排序,并作为ConfigurationClass
进行处理
OrderComparator.sort(candidates);
for (SourceClass candidate : candidates) {
if (this.importStack.contains(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
this.importStack.push(configClass);
try {
processConfigurationClass(candidate.asConfigClass(configClass));
}
finally {
this.importStack.pop();
}
}
}
}
}
处理单独@Bean方法
// Process individual @Bean methods
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
首先获取一个类中的@Bean方法,作为MethodMetadata
对象,然后包装为BeanMethod
对象并添加到org.springframework.context.annotation.ConfigurationClass#beanMethods
属性当中。
/**
* Retrieve the metadata for all <code>@Bean</code> methods.
*/
private Set<MethodMetadata> retrieveBeanMethodMetadata(SourceClass sourceClass) {
AnnotationMetadata original = sourceClass.getMetadata();
Set<MethodMetadata> beanMethods = original.getAnnotatedMethods(Bean.class.getName());
if (beanMethods.size() > 1 && original instanceof StandardAnnotationMetadata) {
// Try reading the class file via ASM for deterministic declaration order...
// Unfortunately, the JVM's standard reflection returns methods in arbitrary
// order, even between different runs of the same application on the same JVM.
try {
AnnotationMetadata asm =
this.metadataReaderFactory.getMetadataReader(original.getClassName()).getAnnotationMetadata();
Set<MethodMetadata> asmMethods = asm.getAnnotatedMethods(Bean.class.getName());
if (asmMethods.size() >= beanMethods.size()) {
Set<MethodMetadata> selectedMethods = new LinkedHashSet<>(asmMethods.size());
for (MethodMetadata asmMethod : asmMethods) {
for (MethodMetadata beanMethod : beanMethods) {
if (beanMethod.getMethodName().equals(asmMethod.getMethodName())) {
selectedMethods.add(beanMethod);
break;
}
}
}
if (selectedMethods.size() == beanMethods.size()) {
// All reflection-detected methods found in ASM method set -> proceed
beanMethods = selectedMethods;
}
}
}
catch (IOException ex) {
logger.debug("Failed to read class file via ASM for determining @Bean method order", ex);
// No worries, let's continue with the reflection metadata we started with...
}
}
return beanMethods;
}
比如解析如下Lite类型的ConfigurationClass
@Component
public class LiteModeBean {
@Scope("prototype")
@Bean
public LiteOrderDao liteOrderDao() {
return new LiteOrderDao();
}
@Bean
public LiteOrderService liteOrderService() {
return new LiteOrderService(liteOrderDao());
}
}
处理单独@Import逻辑
参考博客:注册Bean方式之@Import源码分析
处理单独@ComponentScan 逻辑
- 首先解析注解参数
- 判断当前
SourceClass
是否可以用于注册(conditionEvaluator判断类上面的@Conditional条件),如果说SourceClass
都不满足条件,由它引起的各种注册都是无效的,因此也就不需要进行扫描了 - 遍历解析ComponentScan,使用
ComponentScanAnnotationParser
扫描出所有符合条件的类并进行注册,注意这里已经注册了符合条件的类(包含@Configuration、@Component等的类)
// 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) {
// The config class is annotated with @ComponentScan -> perform the scan immediately
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
4. 检查是否符合ConfigurationClass
条件(full or lite)并继续进行解析(按照ConfigurationClass
),递归调用
// Check the set of scanned definitions for any further config classes and parse recursively if needed
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}