java repeatable_java8注解@Repeatable使用技巧

前言

@Repeatable是java8为了解决同一个注解不能重复在同一类/方法/属性上使用的问题。

应用场景

举一个比较贴近开发的例子,在spring/springboot我们引入资源文件可以使用注解@PropertySource

@PropertySource("classpath:sso.properties")

public class Application {

}

复制代码

那要引入多个资源文件怎么办,没事,我把PropertySource中的value设置成String[]数组就行了

public @interface PropertySource {

...

String[] value();

}

复制代码

那么就能这么用

@PropertySource({"classpath:sso.properties","classpath:dubbo.properties","classpath:systemInfo.properties"})

public class Application {

}

复制代码

就spring引入配置文件来讲,肯定是没事问题的。但是如果注解中2个值存在依赖关系,那么这样就不行了。比如下面这个

@Documented

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.TYPE)

//@Repeatable(Validates.class)

public @interface Validate {

/**

* 业务类型

* @return

*/

String bizCode();

/**

* 订单类型

* @return

*/

int orderType();

}

复制代码我玩策略模式的时候喜欢用注解和spring结合做自动策略路由

上面的@validate注解,bizcode和orderType是一对一的关系,我希望可以添加如下的注解

@Validate(bizCode = "fruit",orderType = 1)

@Validate(bizCode = "fruit",orderType = 2)

@Validate(bizCode = "vegetable",orderType = 2)

public class BizLogic2 {

}

复制代码

很抱歉在java8之前,这种方式不行,不过你可以这么做,新建一个如下的注解

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.TYPE)

@Documented

public @interface Validates {

Validate[] value();

}

复制代码注意这个聚合注解默认约束value来存储子注解

然后对应代码修改如下

@Validates(value = {

@Validate(bizCode = "fruit",orderType = 1)

@Validate(bizCode = "fruit",orderType = 2)

@Validate(bizCode = "vegetable",orderType = 2)

})

public class BizLogic2 {

}

复制代码

在java8的@Repeatable出来之后,我们在不改动@Validates的情况下,对@Validate进行修改,增加@Repeatable(Validates.class)

@Documented

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.TYPE)

@Repeatable(Validates.class)

public @interface Validate {

/**

* 业务类型

* @return

*/

String bizCode();

/**

* 订单类型

* @return

*/

int orderType();

}

复制代码

那么就可以在类上使用多个@Validate注解了。

回过头来看下@PropertySource和@PropertySources也是如此

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Repeatable(PropertySources.class)

public @interface PropertySource

/**

* Container annotation that aggregates several {@link PropertySource} annotations.

*

*

Can be used natively, declaring several nested {@link PropertySource} annotations.

* Can also be used in conjunction with Java 8's support for repeatable annotations,

* where {@link PropertySource} can simply be declared several times on the same

* {@linkplain ElementType#TYPE type}, implicitly generating this container annotation.

*

* @author Phillip Webb

* @since 4.0

* @see PropertySource

*/

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface PropertySources复制代码

从上面的注释中可以看到,spring在4.0支持了java8这个特性

原理

那么@Repeatable的原理是啥?

语法糖而已!

我们反编译下面代码

/*@Validates(value = {*/

@Validate(bizCode = "fruit",orderType = 1)

@Validate(bizCode = "fruit",orderType = 2)

@Validate(bizCode = "vegetable",orderType = 2)

/*})*/

public class BizLogic2 {

}

复制代码

发现反编译变成了db887e2aa6c69773d727fae401d293f2.png

语法糖无疑了。

问题来了

那么再反编译下面代码试试

/*@Validates(value = {*/

@Validate(bizCode = "fruit",orderType = 1)

// @Validate(bizCode = "fruit",orderType = 2)

//@Validate(bizCode = "vegetable",orderType = 2)

/*})*/

public class BizLogic2 {

}

复制代码

生成e4f3b0a8a277b8103bc42b6be7b04c23.png

那这样就存在问题了,我以为加了@Repeatable之后@Validate都会生成被语法糖@Validates包裹。没想到它居然这么智能,只有一个@Validate注解的时候居然不转换。

所以使用多个@Validate的时候就会留坑,你需要兼容1个或多个的场景。

推荐方式

直接使用@Validates读取注解代码如下

Validates validates = AnnotationUtils.getAnnotation(BizLogic2.class,Validates.class);

Arrays.stream(validates.value()).forEach(a->{

System.out.println(a.bizCode());

});

复制代码

带兼容的逻辑如下

Validate validate = AnnotationUtils.getAnnotation(BizLogic.class,Validate.class);

if(Objects.nonNull(validate)){

System.out.println(validate.bizCode());

}

Validates validates = AnnotationUtils.getAnnotation(BizLogic2.class,Validates.class);

Arrays.stream(validates.value()).forEach(a->{

System.out.println(a.bizCode());

});

复制代码

如果你是自己写业务的,我觉得第一种方式更加方便。

但是如果你是开发中间件的,那么必须兼容,你永远不知道你的用户会干吗,哈哈。

还有一点需要注意,我的@Validate是被@Component元注解,当多个@Validate语法糖转换成@Validates之后,由于@Validates上没@Component,导致我的bean加载不到spring容器中去

最后

下面是我公众号,大家可以关注下。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值