JAVA:自定义注解和切面

发布于 2021-11-19  812 次阅读


这是在看公司的自定义日志注解后,一点点引申出来的相关知识。

开始之前,先解释几个注解

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());

欢迎欢迎~热烈欢迎~