spring 源码阅读之@Configuration解析

@Configuration解析

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

java 复制代码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {}

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

java 复制代码
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(EnvConfig.class);

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

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

java 复制代码
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方法里有一步操作

java 复制代码
invokeBeanFactoryPostProcessors(beanFactory);

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

ConfigurationClassPostProcessor

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

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

java 复制代码
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 {//获取metadata
         MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);
         metadata = metadataReader.getAnnotationMetadata();

   }
  //解读Configuration注解配置,config能解到证明是一个配置bean
   Map<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解析

java 复制代码
//获取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"})

解析源码

java 复制代码
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 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());
         }
      }
   }
}

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过程中已完成。

相关推荐
q54314708717 小时前
基于Spring Boot 3 + Spring Security6 + JWT + Redis实现登录、token身份认证
spring boot·redis·spring
jwt79392793718 小时前
Spring之DataSource配置
java·后端·spring
逻辑驱动的ken18 小时前
Java高频面试场景题07
java·开发语言·面试·职场和发展·求职招聘·春招
slarymusic18 小时前
解决报错net.sf.jsqlparser.statement.select.SelectBody
java
callJJ18 小时前
JVM 内存区域划分详解——从生活比喻到运行时数据区全景图
java·jvm·面试·内存区域划分
小江的记录本18 小时前
【网络安全】《网络安全与数据安全核心知识体系》(包括数据脱敏、数据加密、隐私合规、等保2.0)
java·网络·后端·python·算法·安全·web安全
北漂Zachary18 小时前
PHP vs Python vs Java:三大编程语言终极对比
java·python·php
Paxon Zhang18 小时前
JavaEE初阶学习web开发的第一步**计算机组成原理,操作系统,进程(基础扫盲)**
java·后端·学习·java-ee
慕容卡卡18 小时前
大模型核心,MCP(模型上下文协议)和Session API
java·开发语言·人工智能·spring boot·spring cloud
zore_c18 小时前
【C++】C++类和对象实现日期类项目——时间计算器!!!
java·c语言·数据库·c++·笔记·算法·排序算法