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的 ...
随机推荐
- git别名;git配置使用shell函数;git别名使用shell函数;git获取当前分支;git alias
获取当前分支 git symbolic-ref -q --short HEAD 2. 在git别名里使用shell函数,$1获取第一个参数的值,$2……$n依次类推,根据自己习惯需要定制 3. 提交r ...
- Understanding Linux CPU stats
Your Linux server is running slow, so you follow standard procedure and run top. You see the CPU met ...
- jstorm简介
最近在研究jstorm,看了很多资料,所以也想分享出来一些. 安装部署 zeromq 简单快速的传输层框架,安装如下: wget http://download.zeromq.org/zeromq-2 ...
- Cesium中导入三维模型方法(dae到glft/bgltf) 【转】
http://blog.csdn.net/l491453302/article/details/46766909 目录(?)[+] Cesium中目前支持gltf和bgltf两种格式.“gltf是kh ...
- Deformable Convolutional Networks-v1-v2(可变形卷积网络)
如何评价 MSRA 视觉组最新提出的 Deformable ConvNets V2? <Deformable Convolutional Networks>是一篇2017年Microsof ...
- listview加载数据
首先我们需要理清思路:使用ListView显示数据是很方便的,ListVIew的数据之间通过适配器adapter去作为桥梁连接起来.当我们需要使用listview显示大量数据的时候,我们需要使用到分页 ...
- windows vs2017环境下编译webkit
源码地址:https://github.com/BlzFans/wke 先看官方的说明: Web和Flash的嵌入式3D游戏,基于WebKit 建筑工作单元 VS2005: 1安装Visual Stu ...
- 15款不容错过的前端开发Javascript和css类库 - 2017版本~
前端的JS和CSS类库日新月异, 在今天这篇介绍中,我们将精挑细选15款超棒的JS/CSS类库, 希望大家在开发过程中会觉得有帮助~ Core UI 基于Bootstrap4的一套UI类库, Core ...
- Spring Boot中Starter是什么
比如我们要在Spring Boot中引入Web MVC的支持时,我们通常会引入这个模块spring-boot-starter-web,而这个模块如果解压包出来会发现里面什么都没有,只定义了一些POM依 ...
- android中RecyclerView控件的列表项横向排列
本文是在上一篇文章的基础上做的修改:android中RecyclerView控件的使用 1.修改列表项news_item.xml:我这里是把新闻标题挪到了新闻图片的下面显示 <?xml vers ...