前言
本文将通过自定义注解与Spring AOP实现Java的程序增强,会对涉及到的元注解
、切面
进行简单的描述。
一、元注解是什么?
元注解(meta-annotation)的作用是在其他注解上注解,用来提供其他注解的类型说明。在自定义注解时,通常都需要使用元注解。本文仅介绍部分元注解,如需了解的小伙伴请自行查找。
@Target
@Target:注解用来限制注解的使用范围,即指定被修饰的注解能用于哪些程序单元
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
/**
* Returns an array of the kinds of elements an annotation type
* can be applied to.
* @return an array of the kinds of elements an annotation type
* can be applied to
*/
ElementType[] value();
}
通过源码可知,@Target
注解的 value 值是一个数组,这也就意味着注解的作用对象可以有多个。 其取值范围都在ElementType这个枚举之中:
枚举值 | 功能描述 |
---|---|
ElementType.Type | 用于修饰类、接口、注解或枚举类型 |
ElementType.FIELD | 用于修饰属性(成员变量),包括枚举常量 |
ElementType.METHOD | 用于修饰方法 |
ElementType.PAPAMETER | 用于修饰参数 |
ElementType.CONSTRUCTOR | 用于修饰构造方法 |
ElementType.LOCAL_VARIABLE | 用于修饰局部变量 |
ElementType.ANNOTATION_TYPE | 用于修饰注解类 |
ElementType.PACKAGE | 用于修饰包 |
ElementType.TYPE_PARAMETER | 用于标注类型参数(1.8之后版本) |
ElementType.TYPE_USE | 能标注任何类型名称(1.8之后版本) |
@Retention
@Retention:注解用于指定被修饰注解的生命周期。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}
通过源码可知,@Retention
注解的 value 其取值范围都在RetentionPolicy 这个枚举之中:
保留策略值 | 功能描述 |
---|---|
Retention.SOURCE | 注解编译时可见,编译完后就被丢弃,一般用于编译器做一些事情 |
Retention.CLASS | 注解在编译期完后存入.class文件中,运行时JVM不可获取注解信息,该策略值也是默认值 |
Retention.RUNTIME | 运行时JVM可以获取注解信息(反射),表示注解会一直起作用 |
二、具体实现
1.自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface VersionComparisonApi {
String version() default "0";
}
2.切面类
@annotation来配置切点,代表我们的AOP切面会切到所有用 @VersionComparisonApi 注解修饰的类。
代码如下(示例):
@Slf4j
@Aspect
@Component
public class VersionComparisonAspect {
@Before("@annotation(com.byx.cygnus.dm.controller.aoptest.VersionComparisonApi)")
public Object appClientVersionCheck(JoinPoint joinPoint) throws Throwable {
long begin = System.currentTimeMillis();
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
VersionComparisonApi versionComparisonApi = method.getAnnotation(VersionComparisonApi.class);
String version = versionComparisonApi.version();
log.info("============请求开始==========");
log.info("请求链接:{}", request.getRequestURI().toString());
log.info("接口描述:{}", version);
log.info("请求类型:{}", request.getMethod());
log.info("请求方法:{}.{}", signature.getDeclaringTypeName(), signature.getName());
log.info("请求IP:{}", request.getRemoteAddr());
log.info("请求入参:{}", JSON.toJSONString(joinPoint.getArgs()));
// Object result = joinPoint.proceed(); // 环绕通知中用于执行被代理对象的方法
long end = System.currentTimeMillis();
log.info("请求耗时:{}ms", end - begin);
// log.info("请求返回:{}", JSON.toJSONString(result));
log.info("=============请求结束===========");
return null;
}
}
其中涉及到的AOP常用注解再次说明一下:
- @Aspect : 指定切面类;
- @Pointcut:公共切入点表达式
- 通知方法
- 前置通知(@Before) 目标方法执行之前,执行注解的内容
- 后置通知(@After)目标方法执行之后,执行注解的内容
- 返回通知 (@AfterReturning)目标方法返回后,执行注解的内容
- 异常通知 (@AfterThrowing)目标方法抛出异常后,执行注解的内容
- 环绕通知 (@Around)目标方法执行前后,分别执行一些代码
注意事项!!!
ProceedingJoinPoint
只能用在around
(环绕通知)中
环绕通知=前置+目标方法执行+后置通知,proceed方法就是用于启动目标方法执行的- 定义好切片类后要将其加入Spring容器内才能使用(可以使用@Component注解)
3.测试类
@RestController
@RequestMapping("/AopTest")
public class Test {
@VersionComparisonApi(version = "1") // 自定义注解使用
@PostMapping("/test")
public Area getArea(@RequestBody Area area){
return area;
}
}
总结
以上就是今天要讲的内容,本文仅仅简单介绍了实现自定义注解与AOP的过程,而其中的未提及的元注解与AOP相关需要小伙伴自行查阅。