@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)。简单的看一下源码: