Spring @EnableXxx註解的使用理解

@EnableXxx註解

Spring有很多@EnableXxx這種形式的註解,類似於可以一鍵打開某項功能,相當於暴露給用戶的一種便捷的配置API,例如 @EnableAsync 激活異步執行能力,@EnableTransactionManagement 開啓方法事務管理能力。

如果自己也想編寫了一個叫做@EnaleHealthCheck的註解,可以一鍵打開程序的健康檢測功能。編寫這種註解本質上是對 @Import 的使用,通過 @Import 背後的框架基礎設施實現必要的bean的裝配,理解 @EnablbeXxx 需要知道 @Import

@Import及ImportSelector,ImportBeanDefinitionRegistrar

@Import 可以和 @Configuration 一起使用,它通常用於導入另一個@Configuration配置類,功能和xml schame配置的 <import/> 相同,也可以把 @Import 當作元註解使用擴展出更方便和具體的註解,@EnablbeXxx 就是這樣的。

@Import 註解的 value() 屬性接受三種類型:

  • @Configuration
  • ImportSelector
  • ImportBeanDefinitionRegistrar

(1)直接提供一個 @Configuration 配置類給 @Import:

@Configuration
@Import(HealthCheckConfig.class) //HealthCheckConfig配置健康檢測相關的bean
public class AppConfig {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(AppConfig.class);
        context.refresh();
    }
}

//HealthCheckConfig
@Configuration
public class HealthCheckConfig {
    @Bean
    public HealthCheckBean healthCheckBean() {
        return new HealthCheckBean();
    }
}

這個示例運行後,Spring容器中會存在一個 HealthCheckBean 對象用於提供健康檢測功能。

(2)提供一個 ImportSelector 實現類給 @Import:

這種用法是通過 selectImports() 方法返回一組 @Configuration 配置類,這比直接提供一個 @Configuration 配置類更靈活,通過實現 ImportSelector 接口,用戶可以用java代碼控制導入哪些配置,具有更靈活的運行行爲。

//主配置類
@Configuration
@Import(EitherXOrYImportSelector.class)
public class AppConfig {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(AppConfig.class);
        context.refresh();
        //容器啓動成功後,XBean或者YBean任意會存在一個
    }
}

@Configuration
public class XBeanConfig {
    @Bean
    public XBean xbean() {
        return new XBean();
    }
}

@Configuration
public class YBeanConfig {
    @Bean
    public YBean ybean() {
        return new YBean();
    }
}

//二選一的ImportSelector實現
public class EitherXOrYImportSelector implements ImportSelector {
    //selectImports通常返回需要的@Configuration配置類全限定名稱
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        int r = new Random().nextInt(2);
        if (r == 0)
            return new String[]{XBeanConfig.class.getCanonicalName()};
        else
            return new String[]{YBeanConfig.class.getCanonicalName()};
    }
}

(3)把@Import當元註解使用形成@EnableXxx:

@EnableXxx 類似的註解就是擴展 @Import 註解的,上述例子中如果想要實現一個 @EnableEither 註解,直接對兩個對象二選一進行註冊,可以這樣使用 @Import:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(EitherXOrYImportSelector.class) //用Import導入EitheXOrYImportSelector
public @interface EnableEither {
    // 設置爲"x",則在容器中註冊XBean,設置爲"y",則在容器中註冊YBean
    String beanName() default "";
}

public class EitherFooOrBarImportSelector implements ImportSelector {
    //通常返回需要的@Configuration配置類
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        Map<String, Object> annotationAttributes =
                importingClassMetadata.getAnnotationAttributes(EnableEither.class.getCanonicalName());
        String beanName = (String) annotationAttributes.get("beanName");
        if ("x".equalsIgnoreCase(beanName))
            return new String[]{XBeanConfig.class.getCanonicalName()};
        else if ("y".equalsIgnoreCase(beanName))
            return new String[]{YBeanConfig.class.getCanonicalName()};
        else
            return new String[]{};
    }
}

// 現在直接使用@EnableEither`註解即可開啓二選一註冊
@Configuration
@EnableEither(beanName = "y") //容器中會註冊YBean
public class AppConfig {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(AppConfig.class);
        context.refresh();
        XOrYObject o = context.getBean(XOrYObject.class);
        System.out.println(o instanceof XBean);
    }
}

@Import的原理分析

@Import 要結合 AnnotationConfigApplicationContext 容器使用,背後仍然是BeanFactoryPostProcessor 在發揮作用,在 refresh 過程中,會調用所有註冊的 BeanFactoryPostProcessor,其中一個是ConfigurationClassPostProcessor, 它專門用於解析 @Configuration 配置元數據並註冊BeanDefinition,具體可以跟蹤 ConfigurationClassPostProcessor 的 postProcessBeanDefinitionRegistry 接口方法的實現方法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章