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