在我们日常开发中,经常使用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能引入的类型,我们可以参考源码进行查看。