这是在看公司的自定义日志注解后,一点点引申出来的相关知识。
开始之前,先解释几个注解
1、@SuppressWarnings(""),括号中的参数是一些具体忽略哪些警告,常用“unchecked“
给编译器一条指令,告诉它对被批注的代码元素内部的某些警告保持静默。
2、切面注解
- @Aspect: 开启切面支持。
- @Before: 前置通知, 在方法执行之前执行
- @After: 后置通知, 在方法执行之后执行 。
- @AfterRunning: 返回通知, 在方法返回结果之后执行
- @AfterThrowing: 异常通知, 在方法抛出异常之后
- @Around: 环绕通知, 围绕着方法执行
重点:都知道,在通知注解中要写切入点表达式,有这些
execution
:用于匹配方法执行的连接点
within
:限制链接点匹配指定的类型
this
:用于匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配,这样就可能包括引入接口也类型匹配
target
:限制链接点匹配目标对象为指定类型的类
@within
:匹配所有使用了xx注解的类(注意是类)
@annotation
: 匹配使用了xx注解的方法(注意是方法)
这里我们将使用到annotation
AOP依赖相关
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.3.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
</dependency>
第一步:先自定义一个注解
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = {ElementType.METHOD})
public @interface AnnotationTest {
enum LOGTYPE {
CORE("CORECONTENT"), NORMAL("NORMALCONTENT");
private String type;
LOGTYPE(String type) {
this.type = type;
}
public String getType() {
return this.type;
}
}
LOGTYPE Type() default LOGTYPE.NORMAL;
}
第二步:定义一个切面类,把带有AnnotationTest注解的方法作为切入点
@Aspect
@Component
//springboot中要注入容器才会生效
public class AspectTest {
@Around("@annotation(Annotation)")
//Annotation这个名字要和下面参数的变量名一致
public Object around(ProceedingJoinPoint joinPoint, AnnotationTest Annotation) throws Throwable {
//AnnotationTest 是注解的名字
Object proceed = joinPoint.proceed();
System.out.println(Annotation.Type());
return proceed;
}
}
补充:
ProceedingJoinPoint和JoinPoint的区别
Proceedingjoinpoint 继承了 JoinPoint。是在JoinPoint的基础上暴露出 proceed 这个方法。proceed很重要,这个是aop代理链执行的方法。
环绕通知=前置+目标方法执行+后置通知,proceed方法就是用于启动目标方法执行的
暴露出这个方法,就能支持 aop:around 这种切面(而其他的几种切面只需要用到JoinPoint,,这也是环绕通知和前置、后置通知方法的一个最大区别。这跟切面类型有关), 能决定是否走代理链还是走自己拦截的其他逻辑。
总结:使用了 ProceedingJoinPoint作为参数的切面 要使用 proceed()的返回值返回(return)
例子:
Object result = null;
//这之间可以写其他的业务代码
try {
result = joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return result;
第三步:获得切点方法名和参数
获取注解
获取注解的其他方法(2021年12月1日更新)
RateLimiter rateLimiter = AnnotationUtils.findAnnotation(method, RateLimiter.class);//RateLimiter 是注解名字
Signature signature = joinPoint.getSignature();//获取封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class等信息
MethodSignature methodSignature = (MethodSignature) signature;//获取方法署名
Method method = methodSignature.getMethod(); //获取方法信息
if (method != null)
{
xxxxxx annoObj= method.getAnnotation(xxxxxx.class);//获取方法的注解
}
return null;
method.getMethod().getName() 方法名
method.getMethod().getParameterTypes() 方法参数
// 获得切点方法名和参数
@SuppressWarnings("unchecked")
protected Method getMethod(JoinPoint jp, Logger log) {
Method invoked = null;
try {
MethodSignature met = (MethodSignature) jp.getSignature();
invoked = jp.getSourceLocation().getWithinType().getMethod(met.getMethod().getName(), met.getMethod().getParameterTypes());
} catch (NoSuchMethodException e) {
log.error("拦截核心日志不能找到拦截的方法", e);
}
return invoked;
}
总结:核心是切面
补充:在注解中定义枚举
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = {ElementType.METHOD})
public @interface AnnotationTest {
enum LOGTYPE {
CORE("CORECONTENT"), NORMAL("NORMALCONTENT");
private String type;
LOGTYPE(String type) {
this.type = type;
}
public String getType() {
return this.type;
}
}
LOGTYPE Type() default LOGTYPE.NORMAL;
}
调用,直接使用即可,因为枚举是单例
System.out.println(Annotation.LOGTYPE.NORMAL);
System.out.println(Annotation.LOGTYPE.NORMAL.getType());
Comments | NOTHING