Spring 自定義Enable註解以及Enable註解擴展

在我們日常開發中,經常使用Enable註解來開啓某些功能。例如

  1. EnableDiscoveryClient
  2. EnableFeignClients
  3. @EnableAuthorizationServer
  4. ....

我們發現這些註解都是用來開啓某些功能的,其實如果我們換句話來說可能更好理解,就是用來加載某些配置的。

那麼我們來自定義一個Enable註解,首先我們需要對註解有一定的瞭解,下面是我摘抄的網上的一段說明。

01) @interface

使用 @interface 定義註解時,意味着它實現了 java.lang.annotation.Annotation 接口,即該註解就是一個Annotation。

定義 Annotation 時,@interface 是必須的。

注意:它和我們通常的 implemented 實現接口的方法不同。Annotation 接口的實現細節都由編譯器完成。通過 @interface 定義註解後,該註解不能繼承其他的註解或接口。

(02) @Documented

類和方法的 Annotation 在缺省情況下是不出現在 javadoc 中的。如果使用 @Documented 修飾該 Annotation,則表示它可以出現在 javadoc 中。

定義 Annotation 時,@Documented 可有可無;若沒有定義,則 Annotation 不會出現在 javadoc 中。

(03) @Target(ElementType.TYPE)

前面我們說過,ElementType 是 Annotation 的類型屬性。而 @Target 的作用,就是來指定 Annotation 的類型屬性。

@Target(ElementType.TYPE) 的意思就是指定該 Annotation 的類型是 ElementType.TYPE。這就意味着,MyAnnotation1 是來修飾"類、接口(包括註釋類型)或枚舉聲明"的註解。

定義 Annotation 時,@Target 可有可無。若有 @Target,則該 Annotation 只能用於它所指定的地方;若沒有 @Target,則該 Annotation 可以用於任何地方。

(04) @Retention(RetentionPolicy.RUNTIME)

前面我們說過,RetentionPolicy 是 Annotation 的策略屬性,而 @Retention 的作用,就是指定 Annotation 的策略屬性。

@Retention(RetentionPolicy.RUNTIME) 的意思就是指定該 Annotation 的策略是 RetentionPolicy.RUNTIME。這就意味着,編譯器會將該 Annotation 信息保留在 .class 文件中,並且能被虛擬機讀取。

定義 Annotation 時,@Retention 可有可無。若沒有 @Retention,則默認是 RetentionPolicy.CLASS。

下面我們說了怎麼自定義一個Enable註解,其實Enable的方式有很多種。

我們先介紹最簡單的一種,直接引入:

/**
 * 開啓天潤外呼
 * @author clark
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(TRCallConfig.class)
public @interface EnableTRCall {
}

引入配置類

/**
 * 天潤外呼配置類
 * @author clark
 */
@EnableConfigurationProperties({ TRCallProperties.class})
public class TRCallConfig {

    @Autowired
    private TRCallProperties trCallProperties;

    /**
     * 實例化天潤外呼業務
     * @return
     */
    @Bean("trCallService")
    CallService trCallService(){
        TRCallServiceImpl trCallService = new TRCallServiceImpl();
        trCallService.setTrCallProperties(trCallProperties);
        return trCallService;
    }
}

那麼這樣我們就實現了一個自定義的Eable註解了,當然這是比較簡單的。

下面我們來說下第二種,根據參數加載不同的配置類的方法。

/**
 * 全局開啓外呼註解:與單個開啓等價
 * @author clark
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(CallConfigurationSelector.class)
public @interface EnableCall {
    /**
     * 開啓類型
     * @return
     */
    String[] type();

}
/**
 * 配置加載
 * @author clark
 */
public class CallConfigurationSelector  implements ImportSelector{

    /**
     * 解析type參數,動態加載配置類
     * @param annotationMetadata
     * @return
     */
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        //獲取註解的屬性集合
        AnnotationAttributes attributes =
                AnnotationAttributes.fromMap(
                        annotationMetadata.getAnnotationAttributes(EnableCall.class.getName(), false));
        //設置type
        String[] types = attributes.getStringArray("type");
        if(types==null||types.length==0){
            throw new IllegalStateException("type is required");
        }
        List<String> results = new ArrayList<>();
        //處理類型
        for(String type : types) {
            CallType callType = CallType.valueOf(type);
            switch (callType) {
                case CDR:
                    results.add(CDRCallConfig.class.getName());
                    break;
                case TKY:
                    results.add(TKYCallConfig.class.getName());
                    break;
                case TR:
                    results.add(TRCallConfig.class.getName());
                    break;
                case ZC:
                    results.add(ZCCallConfig.class.getName());
                    break;
                default:
                    throw new IllegalStateException("no type is matching");
            }
        }
        return results.toArray(new String[results.size()]);
    }

}

對於Import能引入的類型,我們可以參考源碼進行查看。

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