Aspect实现对方法日志的拦截记录
在实际的业务系统中,我们通常都希望程序自动的打印方法的入参和返回值,某些特定的方法可能不想打印返回值(返回数据过大,打印日志影响效率),特有了下面的实现。
1、忽略返回值的java注解类
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; /**
* 不需要打印返回值的log
* @author yangzhilong
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface NotPrintResponseLog {
public boolean value() default false;
}
2、日志记录切面类
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger; 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.stereotype.Component;
import org.springframework.web.multipart.MultipartFile; import com.alibaba.fastjson.JSONObject; import lombok.extern.slf4j.Slf4j; /**
* 记录Rest和service的方法的入参和返回值
* @author yangzhilong
*/
@Aspect
@Component
@Slf4j
public class CommonLogAspect {
@Pointcut(value = "execution(public * com.tomato..*Impl.*(..))")
private void pointcutService() { }
@Pointcut(value = "execution(public * com.tomato..*Rest*.*(..))")
private void pointcutRest() { }
/*
//拦截restmapping注解
@Pointcut(value = "execution(@org.springframework.web.bind.annotation.RequestMapping public * com.tomato..*(..))")
private void pointCut() { } //拦截post注解
@Pointcut(value = "execution(@org.springframework.web.bind.annotation.PostMapping public * com.tomato..*(..))")
private void pointCutPost() { } //拦截get注解
@Pointcut(value = "execution(@org.springframework.web.bind.annotation.GetMapping public * *com.tomato..*(..))")
private void pointCutGet() { } //拦截Delete注解
@Pointcut(value = "execution(@org.springframework.web.bind.annotation.DeleteMapping public * com.tomato..*(..))")
private void pointCutDelete() { } //拦截put注解
@Pointcut(value = "execution(@org.springframework.web.bind.annotation.PutMapping public * com.tomato..*(..))")
private void pointCutPut() { }*/ @Pointcut("pointcutService()|| pointcutRest()")
private void pointcut() {
} @Around(value = "pointcut()")
public Object Around(ProceedingJoinPoint pjp) throws Throwable {
String classAndMethodName = null;
Method currentMethod = null; try {
currentMethod = this.getCurrentMethod(pjp);
classAndMethodName = this.getCurrentCompleteMethodName(pjp);
} catch (Throwable e) {
log.error("初始化日志记录信息时出错", e);
return pjp.proceed();
} // 处理入参
this.processBefore(pjp, classAndMethodName); Object result = null;
try {
// 调用目标方法
result = pjp.proceed();
} catch (Throwable e) {
// 目标方法异常了
log.info("end执行方法:{}发生异常,异常简述:{}", classAndMethodName, e.getMessage());
throw e;
} // 处理返回值
processReturnValue(result, currentMethod, classAndMethodName); return result;
} /**
* 得到当前方法的对象引用
* @param pjp
* @return
* @throws NoSuchMethodException
* @throws SecurityException
*/
private Method getCurrentMethod(ProceedingJoinPoint pjp) throws NoSuchMethodException, SecurityException {
MethodSignature mig = (MethodSignature) pjp.getSignature();
return pjp.getTarget().getClass().getMethod(mig.getName(), mig.getParameterTypes());
} /**
* 得到完整的方法名
* @param pjp
* @return
*/
private String getCurrentCompleteMethodName(ProceedingJoinPoint pjp) {
return pjp.getTarget().getClass() + "的" + pjp.getSignature().getName() + "方法";
} /**
* 处理入参打印
* @param pjp
* @param classAndMethodName
*/
private void processBefore(ProceedingJoinPoint pjp, String classAndMethodName) {
try {
if(null==pjp.getArgs() || pjp.getArgs().length==0) {
log.info("begin执行方法:{},方法无入参", classAndMethodName);
} else {
if(pjp.getArgs().length == 1) {
if (pjp.getArgs()[0] instanceof Serializable) {
if(isFile(pjp.getArgs()[0])) {
log.info("begin执行方法:{},入参为文件类型,文件名为:{}", classAndMethodName, getFileName(pjp.getArgs()[0]));
} else {
log.info("begin执行方法:{},入参为:{}", classAndMethodName, JSONObject.toJSONString(pjp.getArgs()[0]));
}
}
} else {
log.info("begin执行方法:{},有多个入参", classAndMethodName);
List<Object> list = Arrays.asList(pjp.getArgs());
final AtomicInteger index = new AtomicInteger(1);
list.stream().filter(x -> x instanceof Serializable).forEach(x -> {
if(isFile(x)) {
log.info("入参{}:{}", index.get(), getFileName(x));
} else {
log.info("入参{}:{}", index.get(), JSONObject.toJSONString(x));
}
index.incrementAndGet();
});
}
}
} catch (Throwable e) {
log.error("记录入参日志的时候出错:", e);
}
} /**
* 处理返回值
* @param result
* @param currentMethod
* @param classAndMethodName
*/
private void processReturnValue(Object result, Method currentMethod, String classAndMethodName) {
if(null == result) {
return;
}
try {
if(!currentMethod.isAnnotationPresent(NotPrintResponseLog.class) && result instanceof Serializable) {
log.info("end执行方法:{},返回结果:{}", classAndMethodName, JSONObject.toJSONString(result));
}
} catch (Throwable e) {
log.error("记录返回日志的时候出错:", e);
} } /**
* 获取文件上传的文件名
* @param file
* @return
*/
private String getFileName(Object file) {
return null==file ? "空文件" : ((MultipartFile)file).getName();
} /**
* 判断是否是文件类型
* @param obj
* @return
*/
private boolean isFile(Object obj) {
return obj instanceof MultipartFile;
}
}
20180530补充:
在aop的逻辑内,先走@Around注解的方法。然后是@Before注解的方法,然后这两个都通过了,走核心代码,核心代码走完,无论核心有没有返回值,都会走@After方法。然后如果程序无异常,正常返回就走@AfterReturn,有异常就走@AfterThrowing。


Aspect实现对方法日志的拦截记录的更多相关文章
- Apache 日志设置不记录指定文件类型的方法和日志轮
Apache日志精准的记录了Web访问的记录,但对于访问量很大的站来说,日志文件过大对于分析和保存很不方便.可以在http.conf(或虚拟主机设置文件httpd-vhosts.conf)中进行设置, ...
- Lind.DDD.Aspects通过Plugins实现方法的动态拦截~Lind里的AOP
回到目录 .Net MVC之所以发展的如些之好,一个很重要原因就是它公开了一组AOP的过滤器,即使用这些过滤器可以方便的拦截controller里的action,并注入我们自己的代码逻辑,向全局的异常 ...
- Spring Boot 使用 Aop 实现日志全局拦截
前面的章节我们学习到 Spring Boot Log 日志使用教程 和 Spring Boot 异常处理与全局异常处理,本章我们结合 Aop 面向切面编程来实现全局拦截异常并记录日志. 在 Sprin ...
- Springboot 三种拦截Rest API的方法-过滤器、拦截器、切片
过滤器方式实现拦截(Filter) 通过继承Servlet的Filter类来实现拦截: @Component public class TimeFilter implements Filter { @ ...
- tail -fn 1000 test.log | grep '关键字' 按照时间段 sed -n '/2014-12-17 16:17:20/,/2014-12-17 16:17:36/p' test.log /var/log/wtmp 该日志文件永久记录每个用户登录、注销及系统的启动、停机的事件
Linux 6种日志查看方法,不会看日志会被鄙视的 2020-02-11阅读 7.3K0 作为一名后端程序员,和Linux打交道的地方很多,不会看Linux日志,非常容易受到来自同事和面试官的嘲讽 ...
- 30多条mysql数据库优化方法,千万级数据库记录查询轻松解决(转载)
1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索 ...
- SQLServer 2008以上误操作数据库恢复方法——日志尾部备份(转)
问题: 经常看到有人误删数据,或者误操作,特别是update和delete的时候没有加where,然后就喊爹喊娘了.人非圣贤孰能无过,做错可以理解,但不能纵容,这个以后再说,现在先来解决问题. 遇到这 ...
- [转]SQLServer 2008以上误操作数据库恢复方法——日志尾部备份
原文出处:http://blog.csdn.net/dba_huangzj/article/details/8491327 问题: 经常看到有人误删数据,或者误操作,特别是update和delete的 ...
- SQL Server 2008以上误操作数据库恢复方法——日志尾部备份
原文出处:http://blog.csdn.net/dba_huangzj/article/details/8491327 问题: 经常看到有人误删数据,或者误操作,特别是update和delete的 ...
随机推荐
- Kafka深度解析(如何在producer中指定partition)(转)
原文链接:Kafka深度解析 背景介绍 Kafka简介 Kafka是一种分布式的,基于发布/订阅的消息系统.主要设计目标如下: 以时间复杂度为O(1)的方式提供消息持久化能力,即使对TB级以上数据也能 ...
- [转]HTTPS网络流量解密方法探索系列(一)
前言 分析网络流量总是绕不开HTTPS,因其广泛使用甚至是强制使用逐渐被大众熟知,在保证其安全的同时也提高了对流量进行研究的难度.目前解析HTTPS协议的文章很多,有很多不错的文章可以带着入门,老实说 ...
- C# 同一应用程序域不同线程之间的参数传递方式
很久没有写博客了,最近的项目不用写代码.今天没事就看看thread之间的参数传递方式,这里主要适用于运行在不同线程的两个方法之间参数传递.直接看代码 1.方法之间直接传递参数 void DemoPar ...
- [leetcode]Remove Duplicates from Sorted List II @ Python
原题地址:https://oj.leetcode.com/problems/remove-duplicates-from-sorted-list-ii/ 题意: Given a sorted link ...
- Unique Binary Search Trees leetcode java
题目: Given n, how many structurally unique BST's (binary search trees) that store values 1...n? For e ...
- Tensorflow Serving 模型部署和服务
http://blog.csdn.net/wangjian1204/article/details/68928656 本文转载自:https://zhuanlan.zhihu.com/p/233614 ...
- Java-JUC(八):使用wait,notify|notifyAll完成生产者消费者通信,虚假唤醒(Spurious Wakeups)问题出现场景,及问题解决方案。
模拟通过线程实现消费者和订阅者模式: 首先,定义一个店员:店员包含进货.卖货方法:其次,定义一个生产者,生产者负责给店员生产产品:再者,定义一个消费者,消费者负责从店员那里消费产品. 店员: /** ...
- (转)C#垃圾回收机制详解
GC的前世与今生 虽然本文是以.net作为目标来讲述GC,但是GC的概念并非才诞生不久.早在1958年,由鼎鼎大名的图林奖得主John McCarthy所实现的Lisp语言就已经提供了GC的功能,这是 ...
- [Docker] Driver Bridge network for linking containers
In previous postwe have seen how to link two container together by using `--link`: # docker run -d - ...
- 构造并发送Beacon帧以伪造任意WiFi热点
请想象一下这样的情景:你可以任意伪造很多个WiFi热点, 这个技术只能在linux上使用,而且对无线网卡也有一定的挑剔,具体的下面会讲- 阶段一:基本原理 首先需要搞清楚的是,手机.电脑等支持WiFi ...