在我們日常開發中,經常使用Enable註解來開啓某些功能。例如
-
EnableDiscoveryClient
-
EnableFeignClients
-
@EnableAuthorizationServer
- ....
我們發現這些註解都是用來開啓某些功能的,其實如果我們換句話來說可能更好理解,就是用來加載某些配置的。
那麼我們來自定義一個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能引入的類型,我們可以參考源碼進行查看。