天天看點

Spring AOP程式設計記錄通路日志

AOP(Aspect Oriented Programming,面向切面程式設計)

AOP程式設計可以在不改變原代碼的情況下為特定代碼的執行添加前置或後置執行邏輯。

是用于日志記錄非常好的方法

下面是一個利用該方法對項目中提供的公開API接口進行日志記錄的部分代碼:

1、建立一個AOP日志記錄類

package dkcp.utils.openAPI;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

/**
 * 開放API接口通路日志記錄
 * @description	
 * @className LogInfo
 * @author zhangyan
 * @date 2019年1月2日 下午1:37:53
 */
@Aspect
@Component
public class LogInfo {
	SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSSS");
	public static class T{
		public String mathName;		//方法名
		public List<Object> args;	//請求參數
		public Date starTime;		//請求開始時間
		public Object result;		//請求傳回值
		public Date endTime;		//請求結束時間
	}
	T t = new T();

	/**
	 * 前置通知--擷取請求參數的相關資訊
	 * @description	
	 * @author zhangyan
	 * @date 2019年1月3日 上午9:55:50
	 * @param join
	 */
	@Before("execution(public * tms.monalisa.controller.open.OpenAPIController.doAction(..))")
	public void before(JoinPoint join) {
		//擷取方法名
        t.mathName = join.getSignature().getName();
        //擷取參數清單
        t.args = Arrays.asList(join.getArgs());
        // 請求開始執行時間
        t.starTime = new Date();
	}
	
	/**
	 * AOP傳回通知--擷取接口傳回值并将通路日志寫入到檔案中
	 * @description	
	 * @author zhangyan
	 * @date 2019年1月3日 上午9:56:32
	 * @param join
	 * @param result
	 */
	@AfterReturning(value="execution(public * tms.monalisa.controller.open.OpenAPIController.doAction(..))",returning="result")
	public void AfterReturning(JoinPoint join, Object result) {
		// 請求傳回值
		t.result = result;
		// 請求結束時間
		t.endTime = new Date();
		this.logWrite();
	}

	/**
	 * 寫API通路日志至檔案中
	 * @description	
	 * @author zhangyan
	 * @date 2019年1月3日 上午9:55:23
	 */
	private void logWrite() {
		BufferedWriter bw = new BufferedWriter(this.getFile());
		try {
			bw.newLine();
			bw.write("---------------------------------------------------------------");
			bw.newLine();
			bw.write("請求開始時間:\t" + sdf.format(t.starTime));
			bw.newLine();
			bw.write("請求方法名:\t" + t.mathName);
			bw.newLine();
			bw.write("請求API接口名:\t" + t.args.get(0));
			bw.newLine();
			bw.write("請求參數:\t" + t.args.get(1));
			bw.newLine();
			bw.write("響應結束時間:\t" + sdf.format(t.endTime));
			bw.newLine();
			bw.write("響應參數:\t" + t.result);
			bw.newLine();
			bw.write("請求總用時:\t" + (t.endTime.getTime() - t.starTime.getTime()) + "毫秒");
			bw.newLine();
			bw.write("--------------------------------------------------------------");
			bw.newLine();
			bw.flush();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 擷取檔案執行個體--檔案或檔案夾不存在時自動建立
	 * @description	
	 * @author zhangyan
	 * @date 2019年1月3日 上午9:54:16
	 * @return
	 */
	private FileWriter getFile() {
		FileWriter fW = null;
		SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
		String string = sdf.format(new Date());
		String StrPath = "D:/tms.monalisa-logs/openAPI/tms.monalisa.open-" + string + ".log";
		File file = new File("D:/tms.monalisa-logs/openAPI");
		if (!file.exists()) {
			file.mkdirs();
		}
		try {
			fW = new FileWriter(StrPath,true);
		} catch (IOException e) {
			
			e.printStackTrace();
		}
		return fW;
	}
}
           

解釋:

這是一個建立類,類名上的配置是說明要把這個類作為一個切面來看待,方法名上的配置則是約定對哪個包、類、或者方法執行前置操作(即在該塊代碼添加切面)。

以上代碼中寫到的切面有兩個,前置通知(@Before)和傳回通知(@AfterReturning),這兩個方法分别在要添加切面的方法執行之前和方法傳回之後執行,分别記錄方法的參數和傳回值内容。

其中AOP标注中"execution(public * tms.monalisa.controller.open.OpenAPIController.doAction(..))"的含義解釋為:以public修飾的方法,*表示任意傳回值,後面表示包名類名和方法名,括号中的點表示任意參數的方法。是以這句話的含義是:對于tms.monalisa.controller.open包下的OpenAPIController類中以public修飾、方法名為doAction的方法、任意傳回值和參數的方法均有效。

也可以将類名和方法名寫成星号“*”,表示都整個包下的方法都有效,前面的public也可以寫成星号“*”。

是以,程式就會在執行方法public * tms.monalisa.controller.open.OpenAPIController.doAction(..)之前先執行該前置通知的方法,我們即可通過JoinPoint join擷取到方法原方法的參數。

同理,在方法傳回通知的方法中,依然可以擷取方法public * tms.monalisa.controller.open.OpenAPIController.doAction(..)的傳回值。

再後面就是對檔案的相關操作了。

2、在配置檔案中添加相關配置

<context:annotation-config />
    <context:component-scan base-package="dkcp.utils.openAPI"/>  <!-- 自動掃描 -->

    <!-- 使用注解自動生成代理對象 -->
    <aop:aspectj-autoproxy/>
           

解釋:

将AOP日志類加入到Spring的自動管理,這樣在項目啟動的時候就會自動建立該類的執行個體

最後一句這是開啟注解,好讓程式能正确識别注解的意義

拓展:

AOP通知除此之外還有後置通知(@After)、異常通知(@AfterThrowing)和環繞通知(@Around),使用方法大同小異,參照 https://www.cnblogs.com/dreamfree/p/4102619.html