在SpringBoot開發過程,我們經常會遇到@Enable開始的好多注解,比如@EnableEurekaServer、@EnableAsync、@EnableScheduling等,今天我們就來分析下這些注解到底是如何工作的?
本文目錄
一、@Enable*實現的原理二、@Import注解的用法1. 直接導入配置類2. 依據條件選擇配置類3. 動態注冊Bean
一、@Enable*實現的原理
通過這些@Enable*注解的源碼可以看出,所有@Enable*注解里面都有一個@Import注解,而@Import是用來導入配置類的,所以@Enable*自動開啟的實現原理其實就是導入了一些自動配置的Bean。
二、@Import注解的用法
@Import注解允許導入@Configuration類,ImportSelector和ImportBeanDefinitionRegistrar的實現類,等同於正常的組件類。
有以下三種使用方式
1. 直接導入配置類
@EnableEurekaServer使用了這種方式,注解源碼如下:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({EurekaServerMarkerConfiguration.class})
public @interface EnableEurekaServer {
}
可以看到@EnableEurekaServer注解直接導入了配置類EurekaServerMarkerConfiguration,而這個配置類中向spring容器中注冊了一個EurekaServerMarkerConfiguration的Bean。
EurekaServerMarkerConfiguration的源碼如下:
@Configuration
public class EurekaServerMarkerConfiguration {
public EurekaServerMarkerConfiguration() {
}
@Bean
public EurekaServerMarkerConfiguration.Marker eurekaServerMarkerBean() {
return new EurekaServerMarkerConfiguration.Marker();
}
class Marker {
Marker() {
}
}
}
2. 依據條件選擇配置類
@EnableAsync使用了這種方式,注解源碼如下:
@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;
}
EnableAsync注解中導入了AsyncConfigurationSelector,AsyncConfigurationSelector通過條件來選擇需要導入的配置類,繼承AdviceModeImportSelector又實現了ImportSelector接口,接口重寫selectImports方法進行事先條件判斷PROXY或者ASPECTJ選擇不同的配置類。
AsyncConfigurationSelector源碼如下:
public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {
private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME =
"org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";
/**
* Returns {@link ProxyAsyncConfiguration} or {@code AspectJAsyncConfiguration}
* for {@code PROXY} and {@code ASPECTJ} values of {@link EnableAsync#mode()},
* respectively.
*/
@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;
}
}
}
3. 動態注冊Bean
@EnableAspectJAutoProxy使用了這種方式,注解源碼如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
boolean proxyTargetClass() default false;
boolean exposeProxy() default false;
}
EnableAspectJAutoProxy注解中導入了AspectJAutoProxyRegistrar,AspectJAutoProxyRegistrar實現了ImportBeanDefinitionRegistrar接口,在運行時把Bean注冊到spring容器中。
AspectJAutoProxyRegistrar的源碼如下:
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
/**
* Register, escalate, and configure the AspectJ auto proxy creator based on the value
* of the @{@link EnableAspectJAutoProxy#proxyTargetClass()} attribute on the importing
* {@code @Configuration} class.
*/
@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);
}
}
}
}
推薦閱讀
1.Java中Integer.parseInt和Integer.valueOf,你還傻傻分不清嗎?
2.SpringCloud系列-整合Hystrix的兩種方式)
3.SpringCloud系列-利用Feign實現聲明式服務調用)
4.手把手帶你利用Ribbon實現客戶端的負載均》
5.SpringCloud搭建注冊中心與服務注冊
限時領取免費Java相關資料,涵蓋了Java、Redis、MongoDB、MySQL、Zookeeper、Spring Cloud、Dubbo/Kafka、Hadoop、Hbase、Flink等高並發分布式、大數據、機器學習等技術。
關注下方公眾號即可免費領取:
