Spring 之 Enable* 註解的工作原理
阿新 • • 發佈:2018-11-13
通過簡單的@Enable* 來開啟一項功能的支援,從而避免自己配置大量的程式碼降低使用的難度. 通過觀察@Enable*註解的原始碼,發現所有的註解都有一個@Import註解,它是用來匯入配置類的,這也就意味著這些自動開啟的實現其實是匯入了一些自動配置的 bean, 這些匯入的配置方式主要分為以下三種類型.
第一類:直接匯入配置類
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Import(SchedulingConfiguration.class) @Documented public @interface EnableScheduling { }
直接匯入 SchedulingConfiguration,這個類註解了 @Configuration,且註冊了一個 scheduledAnnotationProcessor 的 bean, 原始碼如下:
@Configuration @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public class SchedulingConfiguration { @Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME) @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() { return new ScheduledAnnotationBeanPostProcessor(); } }
第二類:依據條件選擇配置類
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(AsyncConfigurationSelector.class) public @interface EnableAsync { Class<? extends Annotation> annotation() default Annotation.class; boolean proxyTargetClass() default false; AdviceMode mode() default AdviceMode.PROXY; int order() default Ordered.LOWEST_PRECEDENCE; }
AsyncConfigurationSelector 通過條件來選擇需要匯入的配置類, AsyncConfigurationSelector的根介面為 ImportSelector,這個介面需要重寫 selectImports 方法,在此方法內進行條件判斷.此例中,若 adviceMode 為 PROXY, 則返回 ProxyAsyncConfiguration,若 adviceMode 為ASPECTJ , 則返回 AspectJAsyncConfiguration.原始碼如下:
package org.springframework.scheduling.annotation;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.AdviceModeImportSelector;
import org.springframework.lang.Nullable;
public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {
private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME =
"org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";
@Override
@Nullable
public String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
return new String[] { ProxyAsyncConfiguration.class.getName() };
case ASPECTJ:
return new String[] { ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME };
default:
return null;
}
}
}
第三類:動態註冊bean
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
boolean proxyTargetClass() default false;
boolean exposeProxy() default false;
}
AspectJAutoProxyRegistrar 實現了 ImportBeanDefinitionRegistrar 介面,ImportBeanDefinitionRegistrar 的作用是在執行時自動新增 bean 到已有的配置類,通過重寫方法:
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
其中, AnnotationMetadata 引數用來獲得當前配置類上的註解;BeanDefinitionRegistry 引數用來註冊 bean; 原始碼如下:
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
AnnotationAttributes enableAspectJAutoProxy =
AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
if (enableAspectJAutoProxy != null) {
if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
}