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

相关推荐
lizi888881 小时前
单组件的编写
java
java_heartLake1 小时前
设计模式之代理模式
java·设计模式·代理模式
魏 无羡1 小时前
pgsql 分组查询方法
java·服务器·数据库
兩尛2 小时前
java--面向对象编程(中级部分)
java·开发语言
Xxxx. .Xxxx2 小时前
C语言程序设计实验与习题指导 (第4版 )课后题-第二章+第三章
java·c语言·开发语言
姜西西_2 小时前
[Spring]Spring MVC 请求和响应及用到的注解
java·spring·mvc
逸狼2 小时前
【JavaEE初阶】多线程6(线程池\定时器)
java·开发语言·算法
qq_35323353892 小时前
【原创】java+springboot+mysql科研成果管理系统设计与实现
java·spring boot·mysql·mvc·web
dawn1912282 小时前
SpringMVC 入门案例详解
java·spring·html·mvc
极客先躯2 小时前
高级java每日一道面试题-2024年9月16日-框架篇-Spring MVC和Struts的区别是什么?
java·spring·面试·mvc·struts2·框架篇·高级java