AOP切面編程教程

簡介

就是在一端邏輯執行前後 加入對應的邏輯。常見的用法如 用註解動態切換數據源,或者日誌記錄。

利用註解做日誌記錄的功能

  1. 寫一個用於註解的枚舉
/**
 * @Auther: lots
 * @Date: 2020/4/14 10:06
 * @Description:
 */
public enum LogTypeEnum {
    SAVE, //新增
    SELECT,//查詢
    DELETE,//刪除
    UPDATE;//修改
}
  1. 先寫一個註解類log
import com.lots.inputrabbit.entity.LogTypeEnum;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @Auther: lots
 * @Date: 2020/4/13 14:33
 * @Description:
 */

/**       ElementType的取值
 *     1.CONSTRUCTOR:用於描述構造器
 *     2.FIELD:用於描述域
 *     3.LOCAL_VARIABLE:用於描述局部變量
 *     4.METHOD:用於描述方法
 *     5.PACKAGE:用於描述包
 *     6.PARAMETER:用於描述參數
 *     7.TYPE:用於描述類、接口(包括註解類型) 或enum聲明
 */
@Target({ElementType.METHOD,ElementType.TYPE})
/**
 * RetentionPolicy的取值
 * 1、SOURCE:註解只保留在源文件,當Java文件編譯成class文件的時候,註解被遺棄;
 * 2、CLASS:註解被保留到class文件,但jvm加載class文件時候被遺棄,這是默認的生命週期;
 * 3、RUNTIME:註解不僅被保存到class文件中,jvm加載class文件之後,仍然存在;
 */
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
    //請求類型
    LogTypeEnum logType() default LogTypeEnum.SELECT;
    //接口名稱
    String name() default "";

}

  1. Log切面的實現

import cn.hutool.json.JSONUtil;
import com.lots.inputrabbit.annotation.Log;
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.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;


/**
 * @Auther: lots
 * @Date: 2020/4/13 14:41
 * @Description: Log切面類實現
 */
@Aspect   //定義這是一個切面
@Order(100)  //加載的順序 值越小,優先級越高
@Component   //實例化到spring容器中
public class LogAspect {

    /**
     * 切入點   定義切入的範圍 下面的spring的規範
     * 格式: execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)throws-pattern?)
     * 修飾符匹配(modifier-pattern?)
     * 返回值匹配(ret-type-pattern)可以爲*表示任何返回值,全路徑的類名等
     * 類路徑匹配(declaring-type-pattern?)
     * 方法名匹配(name-pattern)可以指定方法名 或者 *代表所有, set* 代表以set開頭的所有方法
     * 參數匹配((param-pattern))可以指定具體的參數類型,多個參數間用“,”隔開,各個參數也可以用“*”來表示匹配任意類型的參數,如(String)表示匹配一個String參數的方法;(*,String) 表示匹配有兩個參數的方法,第一個參數可以是任意類型,而第二個參數是String類型;可以用(..)表示零個或多個任意參數
     * 異常類型匹配(throws-pattern?)
     * 其中後面跟着“?”的是可選項
     * <p>
     * 例如:
     * 1)execution(* *(..)) //表示匹配所有方法
     * <p>
     * 2)execution(public * com. savage.service.UserService.*(..))//表示匹配com.savage.server.UserService中所有的公有方法
     * <p>
     * 3)execution(* com.savage.server..*.*(..)) //表示匹配com.savage.server包及其子包下的所有方法
     *
     *
     *
     * @Pointcut("@annotation(com.lots.inputrabbit.annotation.Log)")  這種可以直接根據標籤切入
     */
//    @Pointcut("execution( * com.lots.inputrabbit..*(..))")
    @Pointcut("@annotation(com.lots.inputrabbit.annotation.Log)")
    public void serviceLog() {

    }

    /**
     * @Before是在所攔截方法執行之前執行一段邏輯。
     * @After 是在所攔截方法執行之後執行一段邏輯。
     * @Around是可以同時在所攔截方法的前後執行一段邏輯。 ProceedingJoinPoint point   point.proceed()-調用方法
     */

    @Around("serviceLog()")
    public Object log(ProceedingJoinPoint point) throws Throwable {
        try {
            // 獲取方法簽名
            MethodSignature signature = (MethodSignature) point.getSignature();
            //java reflect相關類,通過反射得到註解
            Method method = signature.getMethod();
            HttpServletRequest request= ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
            //獲取用戶請求方法的參數並序列化爲JSON格式字符串
            String params = JSONUtil.parse(point.getArgs()).toStringPretty();
            Class<?> targetClass = method.getDeclaringClass();
            StringBuffer classAndMethod = new StringBuffer();
            //獲取類註解Log
            Log classAnnotation = targetClass.getAnnotation(Log.class);
            //獲取方法註解Log
            Log methodAnnotation = method.getAnnotation(Log.class);
            //下面是各類參數的對應
            System.out.println("---------- 請求路徑:" + request.getServletPath());
            System.out.println("---------- 請求參數:" + params.toString());
            System.out.println("---------- ip:" + request.getRemoteAddr());
            System.out.println("---------- 調用方法:" + method.getName());
            System.out.println("---------- 調用類:" + point.getSignature().getDeclaringTypeName());
            System.out.println("---------- 調用類名:" + point.getSignature().getDeclaringType().getSimpleName());
            System.out.println("---------- 註解定義的名稱:" + methodAnnotation.name());
            System.out.println("---------- 註解定義的類型:" + methodAnnotation.logType());
            
            Object proceed = point.proceed();//調用目標方法
            System.out.println("---------- 返回值爲:"+proceed);

        } catch (Exception e) {

        }
        return null;

    }


}
  1. 測試 Controller與Service的表寫

import com.biz.primus.base.util.JSONResult;
import com.biz.primus.ms.base.api.BaseApiController;
import com.lots.inputrabbit.annotation.Log;
import com.lots.inputrabbit.entity.LogTypeEnum;
import com.lots.inputrabbit.service.TestLogService;
import io.swagger.annotations.Api;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;


@RestController
@RequestMapping("soa/testLog/")
@Api("測試切面")
public class TestLogApiController extends BaseApiController {

    @Resource
    private TestLogService testLogService;

    @GetMapping("la")
    @Log(name = "測試接口la",logType = LogTypeEnum.SELECT)
    public JSONResult la(@RequestParam("name") String name){
        return testLogService.la(name);
    }

    @GetMapping("la2")
    @Log(name = "測試接口la2",logType = LogTypeEnum.SELECT)
    public JSONResult la2(@RequestParam("name") String name){
        return testLogService.la2(name);
    }

}

import com.biz.primus.base.util.JSONResult;
import org.springframework.stereotype.Service;

/**
 * @Auther: lots
 * @Date: 2020/4/13 17:39
 * @Description:
 */

@Service
public class TestLogService {

    public JSONResult la(String name){
        return new JSONResult(name);
    }


    public JSONResult la2(String name){
        return new JSONResult(name);
    }
}

  1. 數據輸出結果

---------- 請求路徑:/soa/testLog/la
---------- 請求參數:[
“lots”
]
---------- ip:127.0.0.1
---------- 調用方法:la
---------- 調用類:com.lots.inputrabbit.api.TestLogApiController
---------- 調用類名:TestLogApiController
---------- 註解定義的名稱:測試接口la
---------- 註解定義的類型:SELECT
---------- 返回值爲:{code:0, msg:‘success’, data:lots, ts:1586933801930}

---------- 請求路徑:/soa/testLog/la2
---------- 請求參數:[
“lots”
]
---------- ip:127.0.0.1
---------- 調用方法:la2
---------- 調用類:com.lots.inputrabbit.api.TestLogApiController
---------- 調用類名:TestLogApiController
---------- 註解定義的名稱:測試接口la2
---------- 註解定義的類型:SELECT
---------- 返回值爲:{code:0, msg:‘success’, data:lots, ts:1586933841411}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章