开发者在使用Spring的时候往往会以为加了@Configuration注解的类可以被称之为配置类,但是在Spring中,配置类的定义如下:
1.加了@Configuration注解,并且proxyBeanMethods属性为true的Bean,被称为Full配置类
2.加了@Configuration注解,但是proxyBeanMethods属性不为true的Bean,被称为Lite配置类
3.加了@Component、@ComponentScan、@Import、@ImportResource四个注解的Bean,被称为Lite配置类
4.如果一个Bean,如上都不满足,但是在其方法上存在@Bean注解的,也被称为Lite配置类
1.生成配置类的源码如下(Spring的启动源码太长了,本文只讲解关键点)
主要方法:ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)
Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
// 存在@Configuration,并且proxyBeanMethods不为false(为true或为null)时,就是Full配置类
if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
}
// 存在@Configuration,并且proxyBeanMethods为false时,是lite配置类
// 或者不存在@Configuration,但是只要存在@Component、@ComponentScan、@Import、@ImportResource四个中的一个,就是lite配置类
// 或者不存在@Configuration,只要存在@Bean注解了的方法,就是lite配置类
else if (config != null || isConfigurationCandidate(metadata)) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
}
public static boolean isConfigurationCandidate(AnnotationMetadata metadata) {
// Do not consider an interface or an annotation...
if (metadata.isInterface()) {
return false;
}
// Any of the typical annotations found?
// 只要存在@Component、@ComponentScan、@Import、@ImportResource四个中的一个,就是lite配置类
for (String indicator : candidateIndicators) {
if (metadata.isAnnotated(indicator)) {
return true;
}
}
// Finally, let's look for @Bean methods...
// 只要存在@Bean注解了的方法,就是lite配置类
return hasBeanMethods(metadata);
}
通过分析上面两段代码就能得出最开始的那四条结论,不过还要补充一点的就是,BeanFactoryPostProcessor.class || BeanPostProcessor.class || AopInfrastructureBean.class || EventListenerFactory.class,如果当前正在生成BeanDefinition中的beanClass属性是实现了这四个接口的,那么一律不能当做配置类
2.配置类的解析(ConfigurationClass->就是配置类)
Spring在启动的时候会往里面注册一个ConfigurationClassPostProcessor,然后因为实现了BeanDefinitionRegistryPostProcessor接口,所以会去调用postProcessBeanDefinitionRegistry()方法
protected final void parse(Class<?> clazz, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(clazz, beanName), DEFAULT_EXCLUSION_FILTER);
}
在其对应的解析器ConfigurationClassParser中,其parse方法下的doProcessConfigurationClass()方法就实现了主要的解析逻辑,可以挑选里面部分代码阅读其逻辑
@Component:会去解析其内部类是否是lite配置类,然后进行递归调用,直到最后全部解析完成
private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass,
Predicate<String> filter) throws IOException {
//获取该类的内部类
Collection<SourceClass> memberClasses = sourceClass.getMemberClasses();
if (!memberClasses.isEmpty()) {
List<SourceClass> candidates = new ArrayList<>(memberClasses.size());
for (SourceClass memberClass : memberClasses) {
// 内部类是不是lite配置类
if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) &&
!memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {
candidates.add(memberClass);
}
}
}
}
@ComponentScan:ComponentScan很特殊,他是这些注解里面唯一一个会去注册BeanDefinition的,先遍历获取到的@ComponentScan注解,然后根据调用其parse方法进行一个,parse方法的最后会调用Scanner.scan()方法扫描,然后进行BeanDefinition的注册,注册逻辑在(Spring源码解析)中有体现
for (AnnotationAttributes componentScan : componentScans) {
// 这里就会进行扫描,得到的BeanDefinition会注册到Spring容器中
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
// 检查扫描出来的BeanDefinition是不是配置类(full和lite)
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
parse{
return scanner.doScan(StringUtils.toStringArray(basePackages));
}
@Import (这边放入的是部分源码,我做了截取):第一步是先判断是否实现了ImportSelector接口,如果实现了,那么强转然后再判断是否是延迟的ImportSelector,如果是,则缓存到list中,如果不是,那么立马执行对应的selectImports方法返回字串,然后根据字串生成对应的类,然后递归解析新生成的类中是否存在@Import
private void processImports() {
//互相Import则抛错
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
} else {
this.importStack.push(configClass);
//如果实现了ImportSelector,那么分两种情况,一种是DeferredImportSelector,另一种则是正常的
if (candidate.isAssignable(ImportSelector.class)) {
//强转转成ImportSelector
ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
this.environment, this.resourceLoader, this.registry);
//这种延迟的,内部会直接放到一个list中,等后续在处理
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
} else {
// 如果import的是普通的ImportSelector,那么直接调用其方法返回一个字串,然后解析这些字串
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
// 继续处理selectImports()所返回的类,如果还有这种类型的,那么继续递归调用
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
}
}
}
// 如果import的类实现了ImportBeanDefinitionRegistrar接口
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar = ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class, this.environment, this.resourceLoader, this.registry);
//缓存到importBeanDefinitionRegistrars中,并不会立马执行
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
// 如果import的类就是普通的类
else {
this.importStack.registerImport(currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
// asConfigClass会为@Import进来的Class其生成对应的ConfigurationClass,并且记录它是谁@Import的
processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
}
}
1. 如果导入的是普通ImportSelector,那么会调用selectImports方法将返回的字符串生成类再次调用processImports()
2.如果导入的是特殊ImportSelector,DeferredImportSelector,那么缓存到(ConfigClass.deferredImportSelectors)这个属性中,然后会在最外层的ConfigurationClassParser.parse(Set<BeanDefinitionHolder> configCandidates)方法,也就是当这一轮的所有配置类(在配置类的解析过程中,可能会有新的配置类生成)解析完成之后,最后去执行对应的selectImports方法,也就是1的逻辑
3.如果导入的是普通类,那么会直接把它包装成一个ConfigurationClass,然后再去执行一遍配置类的处理逻辑(就是@Component,@ComponentScan....这些注解的处理逻辑,并且把它加到configurationClasses中),然后再去执行processConfigurationClass(这里是递归,他这里就会先解析导入的类下面有没有要扫描并且生成的配置类,这边要小心)
4.如果导入的是ImportBeanDefinitionRegistrar,那么缓存到(ConfigClass.importBeanDefinitionRegistrars)这个属性中,在这一轮所有的ConfigurationClass生成完之后,再去调用其中的方法registerBeanDefinitions注册BeanDefinition
这三种注解是解析配置类的主要逻辑,其他的注解(比如@ImportResource,类中有@Bean的方法)是如何生成对应的ConfigurationClass的,读者可以自行解读一下后续篇章会讲解除了@ComponentScan之外,其他注解生成ConfigurationClass之后后续是如何生成BeanDefinition的(包括@import的第四步到底是如何执行的),并且@Bean对应的逻辑又该如何执行
总结:
@Component:检查内部类是否是配置类
@ComponentScan:扫描路径并且注册BeanDefinition,并且检查是否存在配置类,递归解析新配置类
@Import:processImports方法是核心逻辑
1.如果只实现了普通的ImportSelector接口,那么对返回的字符串进行类生成然后继续递归解析(执行processImports方法)
2.如果实现了DeferredImportSelector接口,那么缓存到deferredImportSelectors中,等这一轮配置类(在配置类的解析过程中,可能会有新的配置类生成)最后在执行selectImports方法
3.如果是普通类型,则直接生成ConfigurationClass并且去优先处理导入的这个类的配置类的处理逻辑,然后存放到configurationClasses中
4.如果导入的是ImportBeanDefinitionRegistrar,那么缓存到(ConfigClass.importBeanDefinitionRegistrars)这个属性中,到这一轮配置类解析完之后,再去调用其registerBeanDefinitions注册生成BeanDefinition