Spring IOC @Configuration注解分析

打印 上一主题 下一主题

主题 975|帖子 975|积分 2929

引入

在使用SpringBoot开发时,最常用的注解有@Component、@Service、@Controller、@Configuration等。当类使用这些注解标记时,类会被Spring IOC容器管理,包括创建,填充属性和实例化。
但是Spring容器如何发现并将这些类放到容器进行管理呢?
今天这篇博客主要分析Spring如何处理@Configuration注解。
@Configuration定义
  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Component
  5. public @interface Configuration {
  6.         @AliasFor(annotation = Component.class)
  7.         String value() default "";
  8.        
  9.         //Since: 5.2
  10.         boolean proxyBeanMethods() default true;
  11.         //Since: 6.0
  12.         boolean enforceUniqueMethods() default true;
  13. }
复制代码
看Configuration注解的定义,它本质也是一个@Component注解。
所以,标记有@Configuration注解的类在Spring注册bean的第一阶段就会被注册到容器中,但是使用@Configuration注解的类内部的其他bean并没有被注册到容器中。
想知道@Configuration注解被如何处理,需要看invokeBeanFactoryPostProcessors 方法。
ConfigurationClassPostProcessor处理@Configuration注解

先大致说一下invokeBeanFactoryPostProcessors 方法的基本流程,因为这个方法会处理所有BeanDefinitionRegistryPostProcessor和BeanFactoryPostProcessor接口的子类:

  • 先处理方法入参:beanFactoryPostProcessors(如果只是分析AnnotationConfigApplicationContext,这个参数是空的。只有在分析SpringBoot的启动过程时,这个参数才会有值)
  • 再处理BeanDefinitionRegistryPostProcessor的子类

    • 这里最重要的一件事情是调用ConfigurationClassPostProcessor的processConfigBeanDefinitions方法。

  • 最后处理BeanFactoryPostProcessor的子类
invokeBeanFactoryPostProcessors 的部分代码:
  1. public static void invokeBeanFactoryPostProcessors(
  2.                 ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
  3.        
  4.         Set<String> processedBeans = new HashSet<>();
  5.         if (beanFactory instanceof BeanDefinitionRegistry) {
  6.                 //省略部分代码,处理入参:beanFactoryPostProcessors
  7.         //开始处理BeanDefinitionRegistryPostProcessor接口的子类
  8.         //保存所有符合的子类,即实现BeanDefinitionRegistryPostProcessor,同样实现PriorityOrdered
  9.                 List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
  10.                 String[] postProcessorNames =
  11.                                 beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
  12.                 for (String ppName : postProcessorNames) {
  13.                         if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
  14.                                 currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
  15.                                 processedBeans.add(ppName);
  16.                         }
  17.                 }
  18.                 sortPostProcessors(currentRegistryProcessors, beanFactory);
  19.                 registryProcessors.addAll(currentRegistryProcessors);
  20.         //调用currentRegistryProcessors中所有的BDRPP
  21.                 invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
  22.                 currentRegistryProcessors.clear();
  23.                 //省略部分代码,同样处理BeanDefinitionRegistryPostProcessor接口的子类
  24.         }
  25.         else {
  26.                 //省略部分代码
  27.         }
  28.         //省略部分代码 处理BeanFactoryPostProcessor接口的子类
  29. }
复制代码
以上是invokeBeanFactoryPostProcessors的部分代码,接下来看ConfigurationClassPostProcessor的processConfigBeanDefinitions方法,在这个方法里开始正式地处理@Configruation注解:
  1. public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
  2.         List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
  3.         String[] candidateNames = registry.getBeanDefinitionNames();
  4.         //遍历所有的候选者
  5.         for (String beanName : candidateNames) {
  6.                 BeanDefinition beanDef = registry.getBeanDefinition(beanName);
  7.                 if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
  8.                         if (logger.isDebugEnabled()) {
  9.                                 logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
  10.                         }
  11.                 }
  12.                 //校验候选者类上是否使用了@Configuration注解
  13.                 else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
  14.                         configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
  15.                 }
  16.         }
  17.         //如果configCandidates为空,则立即返回
  18.         if (configCandidates.isEmpty()) {
  19.                 return;
  20.         }
  21.         configCandidates.sort((bd1, bd2) -> {
  22.                 int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
  23.                 int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
  24.                 return Integer.compare(i1, i2);
  25.         });
  26.         SingletonBeanRegistry sbr = null;
  27.         if (registry instanceof SingletonBeanRegistry) {
  28.                 sbr = (SingletonBeanRegistry) registry;
  29.                 if (!this.localBeanNameGeneratorSet) {
  30.                         BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(
  31.                                         AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
  32.                         if (generator != null) {
  33.                                 this.componentScanBeanNameGenerator = generator;
  34.                                 this.importBeanNameGenerator = generator;
  35.                         }
  36.                 }
  37.         }
  38.         if (this.environment == null) {
  39.                 this.environment = new StandardEnvironment();
  40.         }
  41.         // 创建一个解析@Configuration的解析器
  42.         ConfigurationClassParser parser = new ConfigurationClassParser(
  43.                         this.metadataReaderFactory, this.problemReporter, this.environment,
  44.                         this.resourceLoader, this.componentScanBeanNameGenerator, registry);
  45.         Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
  46.         Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
  47.         do {
  48.                 StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");
  49.                 //开始解析候选类
  50.                 parser.parse(candidates);
  51.                 parser.validate();
  52.                 Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
  53.                 configClasses.removeAll(alreadyParsed);
  54.                 // Read the model and create bean definitions based on its content
  55.                 if (this.reader == null) {
  56.                         this.reader = new ConfigurationClassBeanDefinitionReader(
  57.                                         registry, this.sourceExtractor, this.resourceLoader, this.environment,
  58.                                         this.importBeanNameGenerator, parser.getImportRegistry());
  59.                 }
  60.                 //注册解析后的类到容器中
  61.                 this.reader.loadBeanDefinitions(configClasses);
  62.                 alreadyParsed.addAll(configClasses);
  63.                 processConfig.tag("classCount", () -> String.valueOf(configClasses.size())).end();
  64.                 candidates.clear();
  65.                 if (registry.getBeanDefinitionCount() > candidateNames.length) {
  66.                         String[] newCandidateNames = registry.getBeanDefinitionNames();
  67.                         Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
  68.                         Set<String> alreadyParsedClasses = new HashSet<>();
  69.                         for (ConfigurationClass configurationClass : alreadyParsed) {
  70.                                 alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
  71.                         }
  72.                         for (String candidateName : newCandidateNames) {
  73.                                 if (!oldCandidateNames.contains(candidateName)) {
  74.                                         BeanDefinition bd = registry.getBeanDefinition(candidateName);
  75.                                         if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
  76.                                                         !alreadyParsedClasses.contains(bd.getBeanClassName())) {
  77.                                                 candidates.add(new BeanDefinitionHolder(bd, candidateName));
  78.                                         }
  79.                                 }
  80.                         }
  81.                         candidateNames = newCandidateNames;
  82.                 }
  83.         }
  84.         while (!candidates.isEmpty());
  85.         // Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
  86.         if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
  87.                 sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
  88.         }
  89.         if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
  90.                 // Clear cache in externally provided MetadataReaderFactory; this is a no-op
  91.                 // for a shared cache since it'll be cleared by the ApplicationContext.
  92.                 ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
  93.         }
  94. }
复制代码
这个方法的关键点:

  • 找到所有要解析的类
  • 创建一个解析者
  • 开始解析类

    • 记住,这里只是解析,也就是确认所有需要处理的类或者方法,这些类或者方法最终也会注册到容器中

  • 注册解析后的类到容器中:this.reader.loadBeanDefinitions(configClasses);
解析

那么实际上如何解析呢?
  1. protected final SourceClass doProcessConfigurationClass(
  2.                 ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
  3.                 throws IOException {
  4.         //1. 判断当前类是否有内部类,如果有内部类是否使用了@Configuration注解,如果使用了该注解,那么需要先处理内部类的配置
  5.         //存在这种可能,但是实际开发中很少使用
  6.         if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
  7.                 processMemberClasses(configClass, sourceClass, filter);
  8.         }
  9.         // 处理@PropertySource注解
  10.         for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
  11.                         sourceClass.getMetadata(), PropertySources.class,
  12.                         org.springframework.context.annotation.PropertySource.class)) {
  13.                 if (this.environment instanceof ConfigurableEnvironment) {
  14.                         processPropertySource(propertySource);
  15.                 }
  16.                 else {
  17.                         logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
  18.                                         "]. Reason: Environment must implement ConfigurableEnvironment");
  19.                 }
  20.         }
  21.         // 处理@ComponentScan注解
  22.         Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
  23.                         sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
  24.         if (!componentScans.isEmpty() &&
  25.                         !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
  26.                 for (AnnotationAttributes componentScan : componentScans) {
  27.                         Set<BeanDefinitionHolder> scannedBeanDefinitions =
  28.                                         this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
  29.                         for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
  30.                                 BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
  31.                                 if (bdCand == null) {
  32.                                         bdCand = holder.getBeanDefinition();
  33.                                 }
  34.                                 if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
  35.                                         parse(bdCand.getBeanClassName(), holder.getBeanName());
  36.                                 }
  37.                         }
  38.                 }
  39.         }
  40.         // 处理@Import注解
  41.         processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
  42.         // 处理@ImportResource注解
  43.         AnnotationAttributes importResource =
  44.                         AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
  45.         if (importResource != null) {
  46.                 String[] resources = importResource.getStringArray("locations");
  47.                 Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
  48.                 for (String resource : resources) {
  49.                         String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
  50.                         configClass.addImportedResource(resolvedResource, readerClass);
  51.                 }
  52.         }
  53.         // 处理使用了@Bean的方法
  54.         Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
  55.         for (MethodMetadata methodMetadata : beanMethods) {
  56.                 configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
  57.         }
  58.         // Process default methods on interfaces
  59.         processInterfaces(configClass, sourceClass);
  60.         // Process superclass, if any
  61.         if (sourceClass.getMetadata().hasSuperClass()) {
  62.                 String superclass = sourceClass.getMetadata().getSuperClassName();
  63.                 if (superclass != null && !superclass.startsWith("java") &&
  64.                                 !this.knownSuperclasses.containsKey(superclass)) {
  65.                         this.knownSuperclasses.put(superclass, configClass);
  66.                         // Superclass found, return its annotation metadata and recurse
  67.                         return sourceClass.getSuperClass();
  68.                 }
  69.         }
  70.         // No superclass -> processing is complete
  71.         return null;
  72. }
复制代码
processImports方法主要处理两种情况:

  • 实现ImportSelector接口的类
  • 实现ImportBeanDefinitionRegistrar接口的类
但是需要注意,这个方法只是把这些类放到了集合中,但是还没有开始处理。
下面开始看如何处理使用@Bean的方法
  1. @Configuration
  2. @Import(MyBeanFactoryPostProcessor.class)
  3. public class JavaConfig {
  4.     @Bean
  5.     public Country country() {
  6.         return new Country();
  7.     }
  8. }
复制代码
最后返回所有需要处理的方法。
注册
  1. public class MyBeanFactoryPostProcessor implements ImportBeanDefinitionRegistrar {
  2.     @Override
  3.     public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
  4.         ImportBeanDefinitionRegistrar.super.registerBeanDefinitions(importingClassMetadata, registry, importBeanNameGenerator);
  5.         RootBeanDefinition rootBeanDefinition = new RootBeanDefinition();
  6.         rootBeanDefinition.setBeanClass(Person.class);
  7.         registry.registerBeanDefinition("person", rootBeanDefinition);
  8.     }
  9.     @Override
  10.     public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
  11.         ImportBeanDefinitionRegistrar.super.registerBeanDefinitions(importingClassMetadata, registry);
  12.     }
  13. }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

东湖之滨

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表