天天看點

java自定義注解及其功能實作 spring+Aop思路概括demo環境編碼

思路概括

利用面向切面的代理模式(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("切入點執行後 做的事");
    }

}