当前位置:首页 > 编程笔记 > 正文
已解决

spring 源码阅读之@Configuration解析

来自网友在路上 133833提问 提问时间:2023-10-22 11:33:10阅读次数: 33

最佳答案 问答题库338位专家为你答疑解惑

@Configuration解析

@Configuration注解用于标识一个类是配置类,用于声明和组织Bean定义,首先@Configuration本身也是一个@Component,在其注解定义上标有@Component

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {}

通常使用配置类作为参数传递给AnnotationContext,

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(EnvConfig.class);

然后将该类作为一个bean进行初始化,AnnotationConfigApplicationContext在实例化的时候会调用

AnnotationConfigUtils#registerAnnotationConfigProcessors注册必要的bean处理器,这里就会注册一个与解析Configuration相关的类:ConfigurationClassPostProcessor

if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}

ConfigurationClassPostProcessor 实现了BeanDefinitionRegistryPostProcessor,是一个bean定义后置处理器。在sprnig 的容器refresh方法里有一步操作

invokeBeanFactoryPostProcessors(beanFactory);

这里会拿出所有的BeanDefinitionRegistryPostProcessor进行调用其postProcessBeanDefinitionRegistry(registry)方法。这一步发生在bean实例化之前用于扩展beanDef。

ConfigurationClassPostProcessor

后置处理方法会调用postProcessBeanDefinitionRegistry。然后拿出所有的beanDef寻找@Configuration标识的bean。

判断是否是配置bean方法ConfigurationClassUtils#checkConfigurationClassCandidate

public static boolean checkConfigurationClassCandidate(BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {String className = beanDef.getBeanClassName();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...//这几个类型的跳过Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();if (BeanFactoryPostProcessor.class.isAssignableFrom(beanClass) ||BeanPostProcessor.class.isAssignableFrom(beanClass) ||AopInfrastructureBean.class.isAssignableFrom(beanClass) ||EventListenerFactory.class.isAssignableFrom(beanClass)) {return false;}metadata = AnnotationMetadata.introspect(beanClass);}else {//获取metadataMetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);metadata = metadataReader.getAnnotationMetadata();}//解读Configuration注解配置,config能解到证明是一个配置beanMap<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);}else if (config != null || isConfigurationCandidate(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.Integer order = getOrder(metadata);if (order != null) {beanDef.setAttribute(ORDER_ATTRIBUTE, order);}return true;
}

然后解析的到的配置bean会经过ConfigurationClassParser类进行配置解析,具体解析方法在processConfigurationClass,真正解析方法doProcessConfigurationClass

1、PropertySource解析

//获取PropertySource注解
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");}
}

可以在配置bean类上通过@PropertySource注解进行properties配置文件的加载。

@PropertySource("classpath:a.properties")

也可以通过@PropertySources加载多个文件.

2、ComponentScan解析

ComponentScan可以定义扫描bean的包路径,这里spring容器会根据ComponentScan配置的包路径取寻找@Component标注的bean。

@ComponentScan(basePackages = {"com.test.service"})

解析源码

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//会调用scanner.doScan方法进行bean定义的发现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 neededfor (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());}}}
}

3、解析@Import

//getImports方法获取@Import注解引入的类
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

这里判断import的bean是否是ImportSelector、ImportBeanDefinitionRegistrar进行不同的处理,如果都不是安默认按照是一个配置bean进行解析。

4、解析@ImportResource

这个注解主要是引入xml配置进行解析

5、解析方法bean

配置bean里可以通过方法上@Bean注解进行bean的定义

@Bean
public EnvService envService(){EnvService service = new EnvService();return service;
}

通过retrieveBeanMethodMetadata方法获取方法上有@Bean注解的进行解析。

上面解析的几个步骤解析结果会包装成一个ConfigurationClass,然后通过ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsFromImportedResources方法将beandef加载到beanFacotry中。这里ComponentScan注解不会走这里,在scan过程中已完成。

查看全文

99%的人还看了

猜你感兴趣

版权申明

本文"spring 源码阅读之@Configuration解析":http://eshow365.cn/6-21614-0.html 内容来自互联网,请自行判断内容的正确性。如有侵权请联系我们,立即删除!