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能引入的类型,我们可以参考源码进行查看。

发布了192 篇原创文章 · 获赞 42 · 访问量 16万+

猜你喜欢

转载自blog.csdn.net/zhuwei_clark/article/details/105118412