# 实战自定义注解
定义一个注解叫 Encryption,被 Encryption 注解修饰后接口,返回的数据要被加密。
public @interface Encryption {
}
2
在底层实现上,所有定义的注解都会自动继承java.lang.annotation.Annotation接口。
# 编写相应的接口
@Encryption
@GetMapping("/encrypt")
public ResultVo encrypt(){
return ResultVoUtil.success("不一样的科技宅");
}
@GetMapping("/normal")
public ResultVo normal(){
return ResultVoUtil.success("不一样的科技宅");
}
2
3
4
5
6
7
8
9
10
# 编写切面
@Around("@annotation(com.hxh.unified.param.check.annotation.Encryption)")
public ResultVo encryptPoint(ProceedingJoinPoint joinPoint) throws Throwable {
ResultVo resultVo = (ResultVo) joinPoint.proceed();
// 获取注解
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
Encryption annotation = method.getAnnotation(Encryption.class);
// 如果被标识了,则进行加密
if(annotation != null){
// 进行加密
String encrypt = EncryptUtil.encryptByAes(JSON.toJSONString(resultVo.getData()));
resultVo.setData(encrypt);
}
return resultVo;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
这个时候,你会发现返回的数据并没有被加密。 那么这个是为啥呢?俗话说遇到问题不要慌,先掏出手机发个朋友圈(稍微有点跑题了)。出现这个原因是,缺少了`@Retention`` 对@Encryption 的修饰,让我们把它加上。
@Retention(RetentionPolicy.RUNTIME)
public @interface Encryption {
}
2
3
4
TIP
@Retention 作用是什么
Retention 的翻译过来就是"保留"的意思。也就意味着它的作用是,用来定义注解的生命周期的,并且在使用时需要指定 RetentionPolicy,RetentionPolicy 有三种策略,分别是:
- SOURCE - 注解只保留在源文件,当 Java 文件编译成 class 文件的时候,注解被遗弃。
- CLASS - 注解被保留到 class 文件,但 jvm 加载 class 文件时候被遗弃,这是默认的生命周期。
- RUNTIME - 注解不仅被保存到 class 文件中,jvm 加载 class 文件之后,仍然存在。
选择合适的生命周期
首先要明确生命周期 RUNTIME > CLASS > SOURCE 。一般如果需要在运行时去动态获取注解信息,只能使用 RUNTIME。如果要在编译时进行一些预处理操作,比如生成一些辅助代码就用 CLASS。如果只是做一些检查性的操作,比如 @Override 和@SuppressWarnings,则可选用 SOURCE。
我们实际开发中的自定义注解几乎都是使用的 RUNTIME
# @Target
最开始@Encryption 没有使用@Retention 对其生命周期进行定义。所以导致 AOP 在获取的时候一直为空,如果为空就不会对数据进行加密。
是不是感觉这个注解太简陋。那再给他加点东西,加上个@Target。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Encryption {
}
2
3
4
5
- TYPE - 类,接口(包括注解类型)或枚举
- FIELD - 字段(包括枚举常量)
- METHOD - 方法
- PARAMETER - 参数
- CONSTRUCTOR - 构造函数
- LOCAL_VARIABLE - 局部变量
- ANNOTATION_TYPE -注解类型
- PACKAGE - 包
- TYPE_PARAMETER - 类型参数
- TYPE_USE - 使用类型
# @Documented
@Documented 的作用是对自定义注解进行标注,如果使用@Documented 标注了,在生成 javadoc 的时候就会把@Documented 注解给显示出来。没什么实际作用,了解一下就好了。
# @Inherited
被@Inherited 修饰的注解,被用在父类上时其子类也拥有该注解。 简单的说就是,当在父类使用了被@Inherited 修饰的注解@InheritedTest 时,继承它的子类也拥有@InheritedTest 注解。
# 继续改造
需求这个东西经常都在变动。原本需要加密的接口只使用 AES 进行加密,后面又告知有些接口要使用 DES 加密。针对这样的情况,我们可以在注解内,添加一下配置项,来选择使用何种方式加密。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Encryption {
/**
* 加密类型
*/
String value() default "AES";
}
2
3
4
5
6
7
8
9
10
调整接口
@Encryption
@GetMapping("/encrypt")
public ResultVo encrypt(){
return ResultVoUtil.success("不一样的科技宅");
}
@Encryption(value = "DES")
@GetMapping("/encryptDes")
public ResultVo encryptDes(){
return ResultVoUtil.success("不一样的科技宅");
}
@GetMapping("/normal")
public ResultVo normal(){
return ResultVoUtil.success("不一样的科技宅");
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
调整 AOP
@Around("@annotation(com.hxh.unified.param.check.annotation.Encryption)")
public ResultVo encryptPoint(ProceedingJoinPoint joinPoint) throws Throwable {
ResultVo resultVo = (ResultVo) joinPoint.proceed();
// 获取注解
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
Encryption annotation = method.getAnnotation(Encryption.class);
// 如果被标识了,则进行加密
if(annotation != null){
// 进行加密
String encrypt = null;
switch (annotation.value()){
case "AES":
encrypt = EncryptUtil.encryptByAes(JSON.toJSONString(resultVo.getData()));
break;
case "DES":
encrypt = EncryptUtil.encryptByDes(JSON.toJSONString(resultVo.getData()));
break;
default:
break;
}
resultVo.setData(encrypt);
}
return resultVo;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28