配置类解析
在《Spring容器启动(下)》中说到了,Spring启动过程中最核心的方法就是invokeBeanFactoryPostProcessors(),该方法会按照优先级去调用实现了BeanDefinitionRegistryPostProcessor接口和BeanFactoryPostProcessor接口的postProcessBeanDefinitionRegistry()和postProcessBeanFactory(),而这两个方法中,实现了配置类的扫描解析与增强,这篇文章将重点介绍配置类的扫描过程。
PostProcessorRegistrationDelegate类的invokeBeanFactoryPostProcessors()方法中,核心的两个方法是invokeBeanDefinitionRegistryPostProcessors()和invokeBeanFactoryPostProcessors(),这两个方法的源码如下
/**
* Invoke the given BeanDefinitionRegistryPostProcessor beans.
*/
private static void invokeBeanDefinitionRegistryPostProcessors(
Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry, ApplicationStartup applicationStartup) {
for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
StartupStep postProcessBeanDefRegistry = applicationStartup.start("spring.context.beandef-registry.post-process")
.tag("postProcessor", postProcessor::toString);
postProcessor.postProcessBeanDefinitionRegistry(registry);
postProcessBeanDefRegistry.end();
}
}
private static void invokeBeanFactoryPostProcessors(
Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {
for (BeanFactoryPostProcessor postProcessor : postProcessors) {
StartupStep postProcessBeanFactory = beanFactory.getApplicationStartup().start("spring.context.bean-factory.post-process")
.tag("postProcessor", postProcessor::toString);
postProcessor.postProcessBeanFactory(beanFactory);
postProcessBeanFactory.end();
}
}
Spring中主要的解析配置类的方法是ConfigurationClassPostProcessor类的postProcessBeanDefinitionRegistry(),而整个解析配置的核心逻辑在processConfigBeanDefinitions()中
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
int registryId = System.identityHashCode(registry);
……
this.registriesPostProcessed.add(registryId);
// 解析配置类
processConfigBeanDefinitions(registry);
}
一、判断配置类
在解析配置类之前,首先需要判断是否该类是否是配置类,只有配置类才需要进行解析
首先获取当前容器中已经添加的BeanDefinitonName,由于此时还没有进行扫描,此时容器中的BeanDefinition只有容器启动时手动注册的配置类的BeanDefinition和Spring自己添加的一些常用的BeanPostProcessor的BeanDefinition,这些BeanDefinition在《Spring容器启动(上)》中有作说明
获取到这些BeanDefiniton后,会去判断是否是配置类,如果是配置类,就添加到configCandidates中,后续遍历解析
ConfigurationClassUtils的checkConfigurationClassCandidate()用于判断是否为配置类
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
String[] candidateNames = registry.getBeanDefinitionNames();
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
// 什么是配置类?
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
……
}
1.1 @Bean对应的类不能为配置类
假设有一个类A,满足属于配置类的条件,但是在类B中,通过@Bean定义了A的实例,那么类A将不能作为配置类处理,通过@Bean生成BeanDefiniton,它的beanClassName为null,或者factoryMethodName不为null
@configuration
Class A{
}
Class B{
@Bean
public A a(){
return new A();
}
}
public static boolean checkConfigurationClassCandidate(
BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
// @Bean定义的配置类Bean是不起作用的
String className = beanDef.getBeanClassName();
if (className == null || beanDef.getFactoryMethodName() != null) {
return false;
}
……
}
1.2 获取注解信息
获取该类的所有注解信息,后面会根据注解来判断该类是否为配置类
// AnnotationMetadata表示某个类的注解信息,但是并一定要加载这个类
AnnotationMetadata metadata;
//省略获取类注解信息的代码,会通过三种判断来获取注解信息
……
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);
metadata = metadataReader.getAnnotationMetadata();
1.3 判断@Configuration注解
如果一个类上加了@Configuration注解,它就是一个配置类,但该注解的proxyBeanMethods属性又将配置类分为Full和lite配置类,具体后面再介绍
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);
}
else {
return false;
}
如果该类没有添加@Configuration注解,则通过isConfigurationCandidate()判断是否为配置类
通过这种方式判断出来的配置类都属于lite类型
如果是一个接口,则不属于配置类;如果有Component、ComponentScan、Import、ImportResource中的任一个注解,就是配置类;如果有@Bean定义的方法,就是一个配置类
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?
for (String indicator : candidateIndicators) {
if (metadata.isAnnotated(indicator)) {
return true;
}
}
// Finally, let's look for @Bean methods...
return hasBeanMethods(metadata);
}
static {
candidateIndicators.add(Component.class.getName());
candidateIndicators.add(ComponentScan.class.getName());
candidateIndicators.add(Import.class.getName());
candidateIndicators.add(ImportResource.class.getName());
}
1.4 设置Order值
判断完是否为配置后,设置BeanDifinition的Order值,用于对配置类进行排序,按优先级进行解析
// It's a full or lite configuration candidate... Let's determine the order value, if any.
Integer order = getOrder(metadata);
if (order != null) {
beanDef.setAttribute(ORDER_ATTRIBUTE, order);
}
return true;
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/112153.html