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

相关推荐
像我这样帅的人丶你还14 小时前
Java 后端详解(四):分页与搜索
java·javascript·后端
她的男孩14 小时前
数据权限为什么不能只靠注解?Forge 的 Mapper 层 SQL 改写源码拆解
java·后端·架构
tntxia15 小时前
Mybatis的日志输入
java
亦暖筑序16 小时前
Java 8老系统Browser Agent实战:三层拦截把AI操作后台变成可审计流程
java·后端·设计模式
用户2986985301419 小时前
Java 实现 Word 文档加密与权限解除
java·后端
Yeats_Liao20 小时前
14:Servlet中的页面跳转-Java Web
java·后端·架构
未秃头的程序猿20 小时前
告别"if-else地狱"!Java 21模式匹配,代码优雅了10倍
java·后端·面试
鹤望兰67520 小时前
字节跳动国际支付-后端开发-三面面经
java
Flittly21 小时前
【AgentScope Java新手村系列】(14)人机交互
java·spring boot·spring
RainCity21 小时前
Java Swing 自定义组件库分享(十二)
java·笔记·后端