1. 程式人生 > 實用技巧 >【轉】 springboot aop+@interface實現日誌記錄

【轉】 springboot aop+@interface實現日誌記錄

【轉】 springboot aop+@interface實現日誌記錄

一、基本概念

1、自定義註解

自定義註解我們必須瞭解四個元註解,什麼是元註解?元註解指作用於註解之上的元資料或者元資訊,簡單通俗的講,元註解就是註解的註解.

Documented與Inherited是典型的標識性註解,也就是說在註解內部並沒有成員變數,沒有成員變數的註解稱為標識註解

Documented

指明擁有這個註解的元素可以被javadoc此類的工具文件化。這種型別應該用於註解那些影響客戶使用帶註釋的元素宣告的型別。如果一種宣告使用Documented進行註解,這種型別的註解被作為被標註的程式成員的公共API。

Inherited

指明該註解型別被自動繼承。如果使用者在當前類中查詢這個元註解型別並且當前類的宣告中不包含這個元註解型別,那麼也將自動查詢當前類的父類是否存在Inherited元註解,這個動作將被重複執行知道這個標註型別被找到,或者是查詢到頂層的父類。

具體請檢視:https://blog.csdn.net/ab411919134/article/details/81252516

Retention

指明在什麼級別顯示此註解

Retention主要的引數型別包括以下幾種:

RetentionPolicy.SOURCE註解存在於原始碼中,編譯時會被拋棄

RetentionPolicy.CLASS註解會被編譯到class檔案中,但是JVM會忽略

RetentionPolicy.RUNTIME JVM會讀取註解,同時會儲存到class檔案中

Target

指明該型別的註解可以註解的程式元素的範圍

Target主要的引數型別包括以下幾種:

ElementType.TYPE用於類,介面,列舉但不能是註解

ElementType.FIELD作用於欄位,包含列舉值

ElementType.METHOD作用於方法,不包含構造方法

ElementType.PARAMETER作用於方法的引數

ElementType.CONSTRUCTOR作用於構造方法

ElementType.LOCAL_VERIABLE作用於本地變數或者catch語句

ElementType.ANNOTATION_TYPE作用於註解

ElementType.PACKAGE作用於包

2、@ControllerAdvice 註解

在spring 3.2中,新增了@ControllerAdvice 註解,可以用於定義@ExceptionHandler、@InitBinder、@ModelAttribute,並應用到所有@RequestMapping中

二、完整示例

1、工程結構

2、pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.aop.demo</groupId>
    <artifactId>aop-demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.4.1.RELEASE</version>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--spring切面aop依賴-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
    </dependencies>


</project>

3、新建啟動類

package com.aop.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @author Administrator
 * @date 2018/08/18
 */
@SpringBootApplication
public class Application {
    public static void main(String[] args) throws Exception {
        SpringApplication.run(Application.class, args);
    }
}

4、自定義異常類

package com.aop.demo.exception;

/**
 * @author Administrator
 * @date 2018/08/18
 */
public class OperateException extends RuntimeException{

    public Integer errorCode;
    public String errorMessage;

    public OperateException(Integer errorCode, String errorMessage){
        this.errorCode = errorCode;
        this.errorMessage = errorMessage;
    }

    public Integer getErrorCode() {
        return errorCode;
    }

    public void setErrorCode(Integer errorCode) {
        this.errorCode = errorCode;
    }

    public String getErrorMessage() {
        return errorMessage;
    }

    public void setErrorMessage(String errorMessage) {
        this.errorMessage = errorMessage;
    }
}

5、自定義異常處理類,使用了@ControllerAdvice,來捕捉全域性異常

package com.aop.demo.exception;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.HashMap;
import java.util.Map;

/**
 * @author Administrator
 * @date 2018/08/18
 */
@ControllerAdvice
public class MyExceptionHandler {
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public Map<String,Object> exceptionProcess(Exception e){
        Map<String,Object> map = new HashMap<String, Object>();
        if(e instanceof OperateException){
            OperateException myException = (OperateException) e;
            System.out.println(myException.getErrorCode()+": "+ myException.getErrorMessage());
            map.put(myException.getErrorCode().toString(),myException.getErrorMessage());
            return map;
        }else {
            System.out.println("");
            map.put("505","系統級錯誤");
            return map;
        }
    }
}

6、引入aop來處理方法,執行前,執行中,執行後的動作。這裡只是在控制檯列印資訊

1)定義了一個切入點:logwrite(),對com.aop.demo.controller裡面的所有方法有效

舉例說明execution表示式:

任意公共方法的執行:

execution(public * *(..))

任何一個以“set”開始的方法的執行:

execution(* set*(..))

AccountService 介面的任意方法的執行:

execution(* com.xyz.service.AccountService.*(..))

定義在service包裡的任意方法的執行:

execution(* com.xyz.service.*.*(..))

定義在service包和所有子包裡的任意類的任意方法的執行:

execution(* com.xyz.service..*.*(..))

定義在pointcutexp包和所有子包裡的JoinPointObjP2類的任意方法的執行:

execution(* com.test.spring.aop.pointcutexp..JoinPointObjP2.*(..))")

package com.aop.demo.aspect;

import com.aop.demo.controller.MyController;
import com.aop.demo.exception.MyExceptionHandler;
import com.aop.demo.model.LogWrite;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
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;

/**
 * @author Administrator
 * @date 2018/08/20
 */
@Aspect
@Component
public class HttpAspect {

    @Autowired
    private MyExceptionHandler myExceptionHandler;

    @Pointcut("execution(public * com.aop.demo.controller.*.*(..))")
    public void logwrite(){

    }

    @Before("logwrite()")
    public void doBefore(JoinPoint joinPoint){
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        System.out.println("===========================以下是Aspect的Before方法的執行結果==========================");
        //url
        System.out.println("url="+request.getRequestURL());
        //method
        System.out.println("method="+request.getMethod());
        //ip
        System.out.println("id="+request.getRemoteAddr());
        //獲取類名和方法名
        System.out.println("class_method="+joinPoint.getSignature().getDeclaringTypeName() + "," + joinPoint.getSignature().getName());
        //args[]
        System.out.println("args="+joinPoint.getArgs());

        for (Method method: MyController.class.getDeclaredMethods()) {
            if(method.getName().equals(joinPoint.getSignature().getName())){
                LogWrite logWrite = method.getAnnotation(LogWrite.class);
                if(logWrite!=null){
                    System.out.println("Found LogWrite:"+logWrite.user()+" "+logWrite.action() +"處理中");
                }
            }
        }
    }

    @Around("logwrite()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        Object result = null;
        try {
            result = proceedingJoinPoint.proceed();
            return result;
        } catch (Exception e) {
            System.out.println("===========================以下是Aspect的doAround方法捕捉到的異常==========================");
            for (Method method: MyController.class.getDeclaredMethods()) {
                if(method.getName().equals(proceedingJoinPoint.getSignature().getName())){
                    LogWrite logWrite = method.getAnnotation(LogWrite.class);
                    if(logWrite!=null){
                        System.out.println("Found LogWrite:"+logWrite.user()+" "+logWrite.action() +"處理失敗");
                    }
                }
            }
            return myExceptionHandler.exceptionProcess(e);
        }
    }

    @AfterReturning(pointcut = "logwrite()",returning = "object")//列印輸出結果
    public void doAfterReturing(JoinPoint joinPoint,Object object){
        System.out.println("===========================以下是Aspect的doAfterReturing方法的執行結果==========================");
        for (Method method: MyController.class.getDeclaredMethods()) {
            if(method.getName().equals(joinPoint.getSignature().getName())){
                LogWrite logWrite = method.getAnnotation(LogWrite.class);
                if(logWrite!=null){
                    System.out.println("Found LogWrite:"+logWrite.user()+" "+logWrite.action() +"處理完成");
                }
            }
        }
        System.out.println("response="+object.toString());
    }
}

7、自定義註解

package com.aop.demo.model;

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

/**
 * @author Administrator
 * @date 2018/08/18
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogWrite {

    public String user() default "adming";

    public String action() default  "create";
}

8、controller

package com.aop.demo.controller;

import com.aop.demo.exception.OperateException;
import com.aop.demo.model.LogWrite;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author Administrator
 * @date 2018/08/18
 */
@RestController
@RequestMapping("/test")
public class MyController {

    @RequestMapping(value = "/aopsuccess",method = RequestMethod.GET)
    @LogWrite(user = "boshen",action = "excute aopsuccess")
    public String testSucces(){
        return "aopsuccess excute success";
    }

    @RequestMapping(value = "/aoperror/{flag}",method = RequestMethod.GET)
    @LogWrite(user = "boshen",action = "excute aoperror")
    public String testError(@PathVariable String flag){
        if(flag.equals("A")){
            throw new OperateException(1001,"發生業務錯誤");
        }
        return "testError excute success";
    }
}

說明:MyController類裡面使用了自定義註解:@LogWrite,在HttpAspect 類裡面,獲取註解的內容得到user和action,列印到控制檯上

9、用postman執行

控制檯如下:

===========================以下是Aspect的Before方法的執行結果==========================
url=http://10.111.131.11:8084/aop-test/test/aopsuccess
method=GET
id=10.111.131.11
class_method=com.aop.demo.controller.MyController,testSucces
args=[Ljava.lang.Object;@3b3ffba0
Found LogWrite:boshen excute aopsuccess處理中
===========================以下是Aspect的doAfterReturing方法的執行結果==========================
Found LogWrite:boshen excute aopsuccess處理完成
response=aopsuccess excute success

控制檯如下:

===========================以下是Aspect的Before方法的執行結果==========================
url=http://10.111.131.11:8084/aop-test/test/aoperror/A
method=GET
id=10.111.131.11
class_method=com.aop.demo.controller.MyController,testError
args=[Ljava.lang.Object;@67ca9239
Found LogWrite:boshen excute aoperror處理中
===========================以下是Aspect的doAround方法捕捉到的異常==========================
Found LogWrite:boshen excute aoperror處理失敗
1001: 發生業務錯誤
===========================以下是Aspect的doAfterReturing方法的執行結果==========================
Found LogWrite:boshen excute aoperror處理完成
response={1001=發生業務錯誤}