java自定义注解及其功能实现 spring+Aop

思路概括

利用面向切面的代理模式(aop),将 凡是被 自定义注解 标注的方法/类 ,都被代理。在元方法中写正常的业务逻辑,在代理类中利用反射 写上注解的功能。
ps: 非spring项目 自定义注解功能 也可由代理来实现。

demo环境

springboot + aop ,使用业务行为记录功能
aop依赖


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

编码

自定义注解

package li.ql.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author liql
 * @date 2021/4/12
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogAnnotation {
    //自定义注解 相应的功能 利用反应在代理类中书写 
    String value() default "";
}

定义切面 +功能

以自定义的注解为切入点

package li.ql.aspect;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * @author liql
 * @date 2021/9/11
 */
@Aspect //@Aspect注释告诉Spring这是个切面类
@Component
@Slf4j
public class ActionAspect3 {

    //以注解为切入点
    @Pointcut("@annotation(li.ql.annotation.LogAnnotation)")
    public void logPointCut(){}

    @Around("logPointCut()")
    public void beforeMehtod(ProceedingJoinPoint pj) throws Throwable {
        log.info("自定义的切面3 上环绕通知");
        log.info("在这里利用反射可以书写 注解的功能");
        pj.proceed();
        log.info("在这里利用反射可以书写 注解的功能");
        log.info("自定义切面3 下环绕通知");

    }
}


最后 在需要用到注解的类/方法上注上注解即可使用

自定义一个日志记录功能注解

package li.ql.aspect;

import li.ql.annotation.LogAnnotation;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * @author liql
 * @date 2021/4/12
 */
@Aspect //@Aspect注释告诉Spring这是个切面类
@Component
@Slf4j
public class ActionAspect2 {

    //@Pointcut 声明切入点  这个切入点是个注解
    //如果注解上没有其它内容,只要进入这个切面,及代理成功,就可以写业务了
    //如果注解上还有其它的内容 则需要通过反射 获取主街上的内容
    @Pointcut("@annotation(li.ql.annotation.LogAnnotation)")
    public void logPointCut(){}

    @Around(value = "logPointCut()") //在logPointCut()这个切入点方法执行前 需要做的事
    public void beforeMethod(ProceedingJoinPoint pj) throws Throwable {//ProceedingJoinPoint pj 参数只支持around 环绕通知
        //环绕通知 ,如果不调用 pj.proceed(); 让被代理的切入点继续执行就会阻塞
        log.info("第二个切面拦截注解 Aronud环绕通知第一次做的事");


        //先获取方法  才能检查方法上有没有携带注解
        Signature signature = pj.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        System.out.println("methodSignature="+methodSignature.toString());
        //检查方法上是否有注解 先要获取目标类,然后根据目标方法名和类型 获取类的方法的反射 ,然后才能获取注解,
        // 省略获取类方法的反射,则拿不到注解
    //    LogAnnotation annotation = methodSignature.getClass().getDeclaredAnnotation(LogAnnotation.class);// 这个不行
    //   LogAnnotation annotation = methodSignature.getClass().getDeclaredAnnotation(LogAnnotation.class);// 拿不到

        Object targetClass = pj.getTarget();
        System.out.println(methodSignature.getName()+","+methodSignature.getParameterTypes());
        Method declaredMethod = targetClass.getClass().getDeclaredMethod(methodSignature.getName(), methodSignature.getParameterTypes());
        //这下才能拿到注解
        LogAnnotation annotation = declaredMethod.getAnnotation(LogAnnotation.class);
        log.info("获取的注解:{}",annotation);

        //上面那么多都是为了拿到这个value 踩写的
        String value = annotation.value();
//        System.out.println("注解上的值:"+value);

        log.info("aop获得的注解上的值:{}",value);

        pj.proceed(); //让被拦截的方法继续执行下去



        log.info(" 第二个切面 Aronud环绕通知第二次做的事");
    }

      @After(value = "logPointCut()") //在logPointCut()这个切入点方法执行后 需要做的事
     public void afterMethod(){
          log.info("切入点执行后 做的事");
    }

}


版权声明:本文为qq_45812181原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。