@Enablexxx前缀的注解解析原理

@Enablexxx前缀的注解用于开启某个功能,那其原理应该是什么呢?

1.常见@Enable***注解

首先我们看一下几个常见的@Enable注解:
开启事务:
在这里插入图片描述
绑定@ConfigurationProperties的bean:
在这里插入图片描述
开启feignClient:
在这里插入图片描述

可以看到共同点都是使用了@import注解,那么可以预见在解析@import注解注入了指定类。看一下@impot注解支持的类

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {

	/** 
         * // 支持注入的类列表
	 * {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}
	 * or regular component classes to import.
	 */
	Class<?>[] value();

}

2. @import注解在哪里被解析的,如何解析的?

ConfigurationClassPostProcessor类对其进行了解析。

public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
		PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {
说明:
1. 实现了**Aware的接口,解析时会将这个感兴趣的注入该类
2. 实现了BeanDefinitionRegistryPostProcessor接口(继承BeanFactoryPostProcessor)何时调用?在容器启动刷新时调用

在这里插入图片描述
在容器启动的过程中会先调用postProcessBeanFactory方法,然后是postProcessBeanDefinitionRegistry方法,首先看一下postProcessBeanFactory方法:

      /**
	 * Prepare the Configuration classes for servicing bean requests at runtime
	 * by replacing them with CGLIB-enhanced subclasses.
         *//  准备好Configuration(bean),通过用CGLIB增强的子类替换运行时请求的bean
	 */
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
		int factoryId = System.identityHashCode(beanFactory);   // 获取默认方法的hash值(通过内存地址结算的)
		if (this.factoriesPostProcessed.contains(factoryId)) { // 检测避免重复解析
			throw new IllegalStateException(
					"postProcessBeanFactory already called on this post-processor against " + beanFactory);
		}
		this.factoriesPostProcessed.add(factoryId); // 加入已经调用了的集合
		if (!this.registriesPostProcessed.contains(factoryId)) {
			// BeanDefinitionRegistryPostProcessor hook apparently not supported...
			// Simply call processConfigurationClasses lazily at this point then. // 显然不支持BeanDefinitionRegistryPostProcessor钩子...此时,只需懒惰地调用processConfigurationClasses。
			processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory); // 解析@configure注解和lite configuration class(这里包括了@import注解,接下来重点讲述)
		}

		enhanceConfigurationClasses(beanFactory); // CGLIB增强
		beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory)); // 添加ImportAwareBeanPostProcessor,用于增强的Configurtion class注入beanfactory
	}

具体解析的方法processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory),先看一下是怎么过滤目标BeanDefinition,再看一下怎么解析

/**
	 * Build and validate a configuration model based on the registry of
	 * {@link Configuration} classes.
	 */
	public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
		List<BeanDefinitionHolder> configCandidates = new ArrayList<BeanDefinitionHolder>();
		String[] candidateNames = registry.getBeanDefinitionNames();

		for (String beanName : candidateNames) {
			BeanDefinition beanDef = registry.getBeanDefinition(beanName); 
			if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||              // 检测是否已经解析过了(解析过的bean会带有这两个参数值full和lite)已经存在的打印日志继续下一个循环
					ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
				if (logger.isDebugEnabled()) {
					logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
				}
			}
			else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {  // 是否包含ConfigurationClass指定的注解,重点关注
				configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
			}
		}
		.....             
        .....
// 过滤,是否是configuration class
public static boolean checkConfigurationClassCandidate(
      BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
    
   String className = beanDef.getBeanClassName();
   if (className == null || beanDef.getFactoryMethodName() != null) {
      return false;
   }

   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...
      // since we possibly can't even load the class file for this Class.
      Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();
      metadata = new StandardAnnotationMetadata(beanClass, true);
   }
   else {
      try {
         MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);
         metadata = metadataReader.getAnnotationMetadata();  // 获取类上的注解信息
      }
      catch (IOException ex) {
         if (logger.isDebugEnabled()) {
            logger.debug("Could not find class file for introspecting configuration annotations: " +
                  className, ex);
         }
         return false;
      }
   }

   if (isFullConfigurationCandidate(metadata)) { // 是@configuration注解,设置CONFIGURATION_CLASS_ATTRIBUTE属性为full
      beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL); // 
   }
 // 带有@Component,@ComponentScan,@Import,@ImportResource注解,或者类中有带@Bean注解方法的,设置CONFIGURATION_CLASS_ATTRIBUTE属性为lite
   else if (isLiteConfigurationCandidate(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;
}

这样我们就过滤获取到了 configuration class类了,其中带@Import注解的类也被认为是configuration class。现在我们来看一下一下是怎么解析类的,这里只关注@import注解的解析,代码如下:

// 解析带有@Import注解
rivate void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
      Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
    // 没有@import注入的
   if (importCandidates.isEmpty()) {
      return;
   }
     // 需要时检查循环注入
   if (checkForCircularImports && isChainedImportOnStack(configClass)) {
      this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
   }
   else {
   // 压入import堆栈
      this.importStack.push(configClass);
      try {
         for (SourceClass candidate : importCandidates) {
            // @import的是ImportSelector接口的实现
            if (candidate.isAssignable(ImportSelector.class)) {
               // Candidate class is an ImportSelector -> delegate to it to determine imports
               Class<?> candidateClass = candidate.loadClass(); // 加载class文件
               ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);// 实例化
               ParserStrategyUtils.invokeAwareMethods( 
                     selector, this.environment, this.resourceLoader, this.registry); // 根据实现了**Ware接口,注入感兴趣的bean(包括classLoader,BeanFactory,envirment,resourceLoader)

               if (selector instanceof DeferredImportSelector) { // 批量的解析,与上面逻辑一样
                  this.deferredImportSelectorHandler.handle(
                        configClass, (DeferredImportSelector) selector);
               }
               else {
                  String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); // 返回真正需要的加载配置类的classNames
                  Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames); // 这些类中是否有也需要import
                  processImports(configClass, currentSourceClass, importSourceClasses, false); // 递归
               }
            }
            else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) { // 实现这个接口的用于手动注册配置bean
               // Candidate class is an ImportBeanDefinitionRegistrar ->
               // delegate to it to register additional bean definitions
               Class<?> candidateClass = candidate.loadClass(); 
               ImportBeanDefinitionRegistrar registrar =
                     BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
               ParserStrategyUtils.invokeAwareMethods(
                     registrar, this.environment, this.resourceLoader, this.registry);
               configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
            }
            else {
               // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
               // process it as an @Configuration class // 其他类型的
               this.importStack.registerImport(
                     currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
               processConfigurationClass(candidate.asConfigClass(configClass));
            }
         }
      }
      catch (BeanDefinitionStoreException ex) {
         throw ex;
      }
      catch (Throwable ex) {
         throw new BeanDefinitionStoreException(
               "Failed to process import candidates for configuration class [" +
               configClass.getMetadata().getClassName() + "]", ex);
      }
      finally {
         this.importStack.pop();
      }
   }
}

最后是注册这些被解析的类。到这里我们可以看到,对于@Enablexx注解开启功能一般就是通过@import注解来向容器注入所需类,来开启某项功能。
在这里插入图片描述

3.番外:

1.ConfigurationClassPostProcessor什么时候加入到容器里的?

在这里插入图片描述

2.enhanceConfigurationClasses方法又做了些什么呢?为什么要增强呢?

这里和我们的@import注解无关了,主要是增强了bean标记了full属性的(即该bean含有@configration注解)。因为再带有该注解的类一般都含有带@Bean注解的方法,对于这些方法的返回值需要BeanFactory类来注册到容器当中,而原类中不含有该成员,因此要增强这个类,同时@Bean方法通过MethodInterceptor中获取,使得在同一个文件调用方法取到的都是同一个实例(这个不同与@Component注解下的@Bean)。简单的看一下源码:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值