1. 程式人生 > >SpringBoot中神奇的@Enable*註解?

SpringBoot中神奇的@Enable*註解?

在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等高併發分散式、大資料、機器學習等技術。
關注下方公眾號即可免費領取:

Java碎碎念公眾號