成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

Spring中Enable*功能的使用

dinfer / 3344人閱讀

摘要:和不同,接口可以根據(jù)條件一般是注解的屬性指定需要導(dǎo)入的類抽象類實(shí)現(xiàn)了接口,并限定選擇條件只能是枚舉類,也就是說你自定義的注解必須包含屬性。

@Enable** 注解,一般用于開啟某一類功能。類似于一種開關(guān),只有加了這個(gè)注解,才能使用某些功能。

spring boot 中經(jīng)常遇到這樣的場(chǎng)景,老大讓你寫一個(gè)定時(shí)任務(wù)腳本、開啟一個(gè)spring緩存,或者讓你提供spring 異步支持。你的做法肯定是 @EnableScheduling+@Scheduled,@EnableCaching+@Cache,@EnableAsync+@Async 立馬開始寫邏輯了,但你是否真正了解其中的原理呢?之前有寫過一個(gè)項(xiàng)目,是日志系統(tǒng),其中要提供spring 注解支持,簡化配置,當(dāng)時(shí)就是參考以上源碼的技巧實(shí)現(xiàn)的。

1 原理

先來看@EnableScheduling源碼

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

}

可以看到這個(gè)注解是一個(gè)混合注解,和其他注解的唯一區(qū)別就是多了一個(gè)@Import注解

通過查詢spring api文檔

Indicates one or more @Configuration classes to import. Provides functionality equivalent to the  element in Spring XML. Allows for importing @Configuration classes, ImportSelector and ImportBeanDefinitionRegistrar implementations, as well as regular component classes (as of 4.2; analogous to AnnotationConfigApplicationContext.register(java.lang.Class...)). 表示要導(dǎo)入的一個(gè)或多個(gè)@Configuration類。 提供與Spring XML中的元素等效的功能。 允許導(dǎo)入@Configuration類,ImportSelector和ImportBeanDefinitionRegistrar實(shí)現(xiàn),以及常規(guī)組件類(從4.2開始;類似于AnnotationConfigApplicationContext.register(java.lang.Class <?> ...))。

可以看出,通過這個(gè)注解的作用是導(dǎo)入一些特定的配置類,這些特定類包括三種

@Configuration 注解的類

實(shí)現(xiàn)ImportSelector接口的類

實(shí)現(xiàn)ImportBeanDefinitionRegistrar接口的類

1.1 @Configuration注解類

先來看看導(dǎo)入@Configuration注解的例子,打開SchedulingConfiguration類

發(fā)現(xiàn)他是屬于第一種,直接注冊(cè)了一個(gè)ScheduledAnnotationBeanPostProcessor 的 Bean

簡單介紹一下ScheduledAnnotationBeanPostProcessor這個(gè)類干了什么事,他實(shí)現(xiàn)了BeanPostProcessor類。這個(gè)類可以在bean初始化后,容器接管前實(shí)現(xiàn)自己的邏輯。在bean 初始化之后,通過AnnotatedElementUtils.getMergedRepeatableAnnotations()方法去拿到當(dāng)前bean有@Scheduled和@Schedules注解的方法。如果有的話,將其注冊(cè)到內(nèi)部ScheduledTaskRegistrar變量中,開啟定時(shí)任務(wù)并執(zhí)行。順便說一下,BeanPostProcessor接口對(duì)所有bean適用,每個(gè)要注冊(cè)的bean都會(huì)走一遍postProcessAfterInitialization方法。

可以看出,這種方法適用于初始化時(shí)便獲取到全部想要的信息,如@Scheduled的元數(shù)據(jù)等。同時(shí)需要注意:被注解方法不能有參數(shù),不能有返回值。

1.2 實(shí)現(xiàn)ImportSelector接口的類

再來看看第二種實(shí)現(xiàn)方式,打開EnableAsync類

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

    Class annotation() default Annotation.class;

    boolean proxyTargetClass() default false;

    AdviceMode mode() default AdviceMode.PROXY;
    
    int order() default Ordered.LOWEST_PRECEDENCE;

}

可以看到他通過導(dǎo)入AsyncConfigurationSelector類來開啟異步支持,打開AsyncConfigurationSelector類

public class AsyncConfigurationSelector extends AdviceModeImportSelector {

    private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME =
            "org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";
    @Override
    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;
        }
    }

}

AdviceModeImportSelector是一個(gè)抽象類,他實(shí)現(xiàn)了ImportSelector類的selectImports方法,先來看一下selectImports的api 文檔

Interface to be implemented by types that determine which @Configuration class(es) should be imported based on a given selection criteria, usually one or more annotation attributes. An ImportSelector may implement any of the following Aware interfaces, and their respective methods will be called prior to selectImports(org.springframework.core.type.AnnotationMetadata):

EnvironmentAware

BeanFactoryAware

BeanClassLoaderAware

ResourceLoaderAware

ImportSelectors are usually processed in the same way as regular @Import annotations, however, it is also possible to defer selection of imports until all @Configuration classes have been processed (see DeferredImportSelector for details). 通過一個(gè)給定選擇標(biāo)準(zhǔn)的類型來確定導(dǎo)入哪些@Configuration,他和@Import的處理方式類似,只不過這個(gè)導(dǎo)入Configuration可以延遲到所有Configuration都加載完

總結(jié)起來有一下幾點(diǎn):

selectImports 接口和@Configuration類似,用于導(dǎo)入類。

和@Configuration不同,selectImports 接口可以根據(jù)條件(一般是注解的屬性)指定需要導(dǎo)入的類

AdviceModeImportSelector 抽象類實(shí)現(xiàn)了SelectImports接口,并限定選擇條件只能是AdviceMode枚舉類,也就是說你自定義的注解必須包含AdviceMode屬性。

1.3實(shí)現(xiàn)ImportBeanDefinitionRegistrar接口的類

查看@EnableAspectJAutoProxy

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

    boolean proxyTargetClass() default false;

    boolean exposeProxy() default false;

}

這個(gè)注解導(dǎo)入的時(shí)AspectJAutoProxyRegistrar類,AspectJAutoProxyRegistrar實(shí)現(xiàn)了

ImportBeanDefinitionRegistrar接口,實(shí)現(xiàn)類

    public void registerBeanDefinitions(
            AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

        AnnotationAttributes enableAspectJAutoProxy =
                AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
        if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
            AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
        }
        if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
            AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
        }
    }

我們來看一下ImportBeanDefinitionRegistrar官方api文檔

Interface to be implemented by types that register additional bean definitions when processing @Configuration classes. Useful when operating at the bean definition level (as opposed to @Bean method/instance level) is desired or necessary. Along with @Configuration and ImportSelector, classes of this type may be provided to the @Import annotation (or may also be returned from an ImportSelector).

這個(gè)接口的兩個(gè)參數(shù),AnnotationMetadata 表示當(dāng)前類的注解,BeanDefinitionRegistry 注冊(cè)bean。

可以看出和前兩種方式比,這種方式更加精細(xì),需要你自己去實(shí)現(xiàn)bean的注冊(cè)邏輯。第二種方式只傳入了一個(gè)AnnotationMetadata,返回類全限定名,框架自動(dòng)幫你注冊(cè)。而第三種方式,還傳入了一個(gè)BeanDefinitionRegistry讓你自己去注冊(cè)。

其實(shí)三種方式都能很好的實(shí)現(xiàn)導(dǎo)入邏輯。他們的優(yōu)缺點(diǎn)如下:

@Configuration 需要手動(dòng)判斷如何導(dǎo)入。

SelectImports 封裝較好,可根據(jù)選擇導(dǎo)入,尤其當(dāng)你選擇的條件是AdviceMode,還可以選擇AdviceModeSelector,幾行代碼搞定。

ImportBeanDefinitionRegistrar 最幸苦也最靈活,一些邏輯自己寫。

2 實(shí)踐

最后,我們需要來寫一個(gè)自實(shí)現(xiàn)的@EnableDisconfig功能。disconfig是一種配置中心,我們一般的用法是寫兩個(gè)bean

@Bean(destroyMethod="destroy")
public DisconfMgrBean disconfMgrBean(){
.....
}
@Bean(initMethod="int", destroyMethod="destroy")
public DisconfMgrBeanSecond disconfMgrBeanSecond(){
......
}

每次搭框架這么寫確實(shí)挺費(fèi)事的,即使你記在筆記上了,復(fù)制粘貼也還需要改scan路徑。下面我們用優(yōu)雅的代碼來實(shí)現(xiàn)一下。

2.1 @EnableDisconf

首先定義一個(gè)注解類@EnableDisconf

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({DisconfConfig.class})
public @interface EnableDisconf{
    String scanPackages() default "";
}

接著實(shí)現(xiàn)DisconfConfig類

@Configuration
public class DisconfConfig implements ApplicationContextAware {
    private ApplicationContext applicationContext;

    public DisconfConfig() {
    }

    @Bean(
        destroyMethod = "destroy"
    )
    @ConditionalOnMissingBean
    public DisconfMgrBean disconfMgrBean() {
        DisconfMgrBean bean = new DisconfMgrBean();
        Map bootBeans = this.applicationContext.getBeansWithAnnotation(EnableDisconf.class);
        Set scanPackagesList = new HashSet();
        if (!CollectionUtils.isEmpty(bootBeans)) {
            Iterator var4 = bootBeans.entrySet().iterator();

            while(var4.hasNext()) {
                Entry configBean = (Entry)var4.next();
                Class bootClass = configBean.getValue().getClass();
                if (bootClass.isAnnotationPresent(EnableDisconf.class)) {
                    EnableDisconf enableDisconf = (EnableDisconf)bootClass.getAnnotation(EnableDisconf.class);
                    String scanPackages = enableDisconf.scanPackages();
                    if (StringUtils.isEmpty(scanPackages)) {
                        scanPackages = bootClass.getPackage().getName();
                    }

                    scanPackagesList.add(scanPackages);
                }
            }
        }

        if (CollectionUtils.isEmpty(scanPackagesList)) {
            bean.setScanPackage(System.getProperty("scanPackages"));
        } else {
            bean.setScanPackage(StringUtils.join(scanPackagesList, ","));
        }

        return bean;
    }

    @Bean(
        initMethod = "init",
        destroyMethod = "destroy"
    )
    @ConditionalOnMissingBean
    public DisconfMgrBeanSecond disconfMgrBeanSecond() {
        return new DisconfMgrBeanSecond();
    }

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

這里有兩點(diǎn)需要說明

如果你的jar包是需要給其他人使用,一定要加上@ConditionalOnMissingBean,確保Bean只會(huì)被創(chuàng)建一次。

ApplicationContextAware 這個(gè)類是我們程序感知spring容器上下文的類,簡單來說就是通過類似**Aware這樣的類去拿容器中的信息。感興趣的同學(xué)可以看一下spring中關(guān)于**Aware類的使用。

最后你只需要將項(xiàng)目打成jar包,上傳私服,然后就可以很輕松的使用@Enable帶來的便捷了。

@SpringBootApplication
@EnableDisconf(scanPackages="com.demo")
public class Application{
    public static void main(String[] args){
        SpringApplication.run(Application.class,args);
    }
}

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/74460.html

相關(guān)文章

  • Spring Cloud 注冊(cè)心高可用搭建

    摘要:配置應(yīng)用名稱,在注冊(cè)中心中顯示的服務(wù)注冊(cè)名稱。配置為為喜歡,即連接注冊(cè)中心使用地址形式,也可以使用,但生產(chǎn)環(huán)境不推薦。配置注冊(cè)中心清理無效節(jié)點(diǎn)的時(shí)間間隔,默認(rèn)毫秒,即秒。 Spring Cloud的注冊(cè)中心可以由Eureka、Consul、Zookeeper、ETCD等來實(shí)現(xiàn),這里推薦使用Spring Cloud Eureka來實(shí)現(xiàn)注冊(cè)中心,它基于Netfilix的Eureka做了二次...

    wthee 評(píng)論0 收藏0
  • ★推薦一款適用于SpringBoot項(xiàng)目輕量級(jí)HTTP客戶端框架

    摘要:請(qǐng)求重試攔截器錯(cuò)誤解碼器在發(fā)生請(qǐng)求錯(cuò)誤包括發(fā)生異?;蛘唔憫?yīng)數(shù)據(jù)不符合預(yù)期的時(shí)候,錯(cuò)誤解碼器可將相關(guān)信息解碼到自定義異常中。 在SpringBoot項(xiàng)目直接使用okhttp、httpClient或者RestTemplate發(fā)起HTTP請(qǐng)求,既繁瑣又不方便統(tǒng)一管理。因此,在這里推薦一個(gè)適...

    不知名網(wǎng)友 評(píng)論0 收藏0
  • 深度剖析Spring Boot自動(dòng)裝配機(jī)制實(shí)現(xiàn)原理

    摘要:所以,所謂的自動(dòng)裝配,實(shí)際上就是如何自動(dòng)將裝載到容器中來。實(shí)際上在版本中,模塊驅(qū)動(dòng)注解的出現(xiàn),已經(jīng)有了一定的自動(dòng)裝配的雛形,而真正能夠?qū)崿F(xiàn)這一機(jī)制,還是在版本中,條件注解的出現(xiàn)。,我們來看一下的自動(dòng)裝配是怎么一回事。在前面的分析中,Spring Framework一直在致力于解決一個(gè)問題,就是如何讓bean的管理變得更簡單,如何讓開發(fā)者盡可能的少關(guān)注一些基礎(chǔ)化的bean的配置,從而實(shí)現(xiàn)自動(dòng)裝...

    不知名網(wǎng)友 評(píng)論0 收藏0
  • 2.走向自動(dòng)裝配

    摘要:走向自動(dòng)裝配模式注解裝配走向自動(dòng)裝配課程介紹手動(dòng)裝配自動(dòng)裝配自動(dòng)裝配是以手動(dòng)裝配為基礎(chǔ)實(shí)現(xiàn)的手動(dòng)裝配模式注解模式注解是一種用于聲明在應(yīng)用中扮演組件角色的注解。 2.走向自動(dòng)裝配 Spring 模式注解裝配 2-1 走向自動(dòng)裝配 課程介紹 spring framework手動(dòng)裝配 spring boot自動(dòng)裝配 spring boot自動(dòng)裝配是以spring framework手動(dòng)裝...

    rose 評(píng)論0 收藏0
  • 領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)戰(zhàn)術(shù)模式--領(lǐng)域事件

    摘要:將領(lǐng)域中所發(fā)生的活動(dòng)建模成一系列離散事件。領(lǐng)域事件是領(lǐng)域模型的組成部分,表示領(lǐng)域中所發(fā)生的事情。創(chuàng)建領(lǐng)域事件事件命名在建模領(lǐng)域事件時(shí),我們應(yīng)該根據(jù)限界上下文中的通用語言來命名事件。 使用領(lǐng)域事件來捕獲發(fā)生在領(lǐng)域中的一些事情。 領(lǐng)域驅(qū)動(dòng)實(shí)踐者發(fā)現(xiàn)他們可以通過了解更多發(fā)生在問題域中的事件,來更好的理解問題域。這些事件,就是領(lǐng)域事件,主要是與領(lǐng)域?qū)<乙黄疬M(jìn)行知識(shí)提煉環(huán)節(jié)中獲得。 領(lǐng)域事件,可...

    wzyplus 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<