主要拦截前端或后天的请求,打印请求方法参数、返回值、耗时、异常的日志。方便开发调试,能很快定位到问题出现在哪个方法中。

前端请求拦截,mvc的拦截器

 import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.Set; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.core.NamedThreadLocal;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView; import com.xxx.eduyun.sdk.log.ApplicationLogging;
import com.xxx.flipclass.sdk.client.utils.TimeUtil; /**
* <b>function:</b> spring mvc 请求拦截器
* @author hoojo
* @createDate 2016-11-24 下午3:19:27
* @file MVCRequestInterceptor.java
* @package com.xxx.eduyun.app.mvc.interceptor
* @project eduyun-app-web
* @blog http://blog.csdn.net/IBM_hoojo
* @email hoojo_@126.com
* @version 1.0
*/
public class MVCRequestInterceptor extends ApplicationLogging implements HandlerInterceptor { private static final ObjectMapper mapper = new ObjectMapper();
private NamedThreadLocal<Long> startTimeThreadLocal = new NamedThreadLocal<Long>("StopWatch-startTimed"); @Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { info("##############################【一个MVC完整请求开始】##############################"); info("*******************MVC业务处理开始**********************");
try {
long timed = System.currentTimeMillis();
startTimeThreadLocal.set(timed); String requestURL = request.getRequestURI();
info("当前请求的URL:【{}】", requestURL);
info("执行目标方法: {}", handler); Map<String, ?> params = request.getParameterMap();
if (!params.isEmpty()) {
info("当前请求参数打印:");
print(request.getParameterMap(), "参数");
}
} catch (Exception e) {
error("MVC业务处理-拦截器异常:", e);
}
info("*******************MVC业务处理结束**********************"); return true;
} @Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { info("*******************一个MVC 视图渲染开始**********************"); try {
info("执行业务逻辑代码耗时:【{}】", TimeUtil.formatTime(new Date().getTime() - startTimeThreadLocal.get()));
String requestURL = request.getRequestURI();
info("当前请求的URL:【{}】", requestURL); if (modelAndView != null) {
info("即将返回到MVC视图:{}", modelAndView.getViewName()); if (modelAndView.getView() != null) {
info("返回到MVC视图内容类型ContentType:{}", modelAndView.getView().getContentType());
} if (!modelAndView.getModel().isEmpty()) { info("返回到MVC视图{}数据打印如下:", modelAndView.getViewName());
print(modelAndView.getModel(), "返回数据");
}
}
} catch (Exception e) {
error("MVC 视图渲染-拦截器异常:", e);
} info("*******************一个MVC 视图渲染结束**********************");
} @Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { try {
String requestURL = request.getRequestURI();
info("MVC返回请求完成URL:【{}】", requestURL);
info("MVC返回请求完成耗时:【{}】", TimeUtil.formatTime(new Date().getTime() - startTimeThreadLocal.get()));
if (ex != null) {
info("MVC返回请求发生异常:", ex.getMessage());
error("异常信息如下:", ex);
}
} catch (Exception e) {
error("MVC完成返回-拦截器异常:", e);
} info("##############################【一个MVC完整请求完成】##############################");
} private void print(Map<String, ?> map, String prefix) {
if (map != null) {
Set<String> keys = map.keySet();
Iterator<String> iter = keys.iterator();
while (iter.hasNext()) { String name = iter.next();
if (name.contains("org.springframework.validation.BindingResult")) {
continue;
} String value = "";
try {
value = mapper.writeValueAsString(map.get(name));
} catch (Exception e) {
error("转换参数【{}】发生异常:", name, e);
}
info("{} \"{}\": {}", prefix, name, value);
}
}
}
}

spring-mvc.xml增加配置内容

 <mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/exceltemplate/**" />
<mvc:exclude-mapping path="/statics/**" />
<mvc:exclude-mapping path="/global/**" />
<mvc:exclude-mapping path="/denied/**" />
<mvc:exclude-mapping path="/favicon.ico" />
<mvc:exclude-mapping path="/index.jsp" />
<bean class="com.xxx.eduyun.app.mvc.interceptor.MVCRequestInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>

过滤静态资源,一些静态资源不需要拦截,在这里配置黑名单不让它进入拦截器。

下面是sdk接口拦截器,用到spirng的aop的MethodIntercept

 package com.xxx.flipclass.sdk.framework.aop;

 import java.lang.reflect.Method;
import java.util.Date; import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.codehaus.jackson.map.ObjectMapper; import com.xxx.flipclass.sdk.client.utils.ParameterNameUtils;
import com.xxx.flipclass.sdk.client.utils.TimeUtil; /**
* <b>function:</b> Spring 接口调用拦截器,主要拦截com.xxx.*.sdk.client对外接口
* @author hoojo
* @createDate 2016-11-24 下午5:39:57
* @file ExecutionApiLogMethodInterceptor.java
* @package com.xxx.eduyun.sdk.framework
* @project eduyun-sdk-service
* @blog http://blog.csdn.net/IBM_hoojo
* @email hoojo_@126.com
* @version 1.0
*/
public class ExecutionApiLogMethodInterceptor implements MethodInterceptor { private Logger log = LogManager.getLogger(ExecutionApiLogMethodInterceptor.class);
private static final ObjectMapper mapper = new ObjectMapper(); @Override
public Object invoke(MethodInvocation invocation) throws Throwable { info("************************************【接口调用拦截开始】*************************************");
String targetName = invocation.getThis().getClass().getSimpleName();
Method method = invocation.getMethod();
String methodName = method.getName(); info("系统开始执行方法:{}.{}", targetName, methodName); info("【{}.{}】方法参数打印如下:", targetName, methodName);
Object[] args = invocation.getArguments(); //printArgs(args, method, invocation.getThis().getClass());
printArgs(args, method); try {
long timed = System.currentTimeMillis(); Object result = invocation.proceed(); info("【{}.{}】方法执行完成,耗时:【{}】", targetName, methodName, TimeUtil.formatTime(new Date().getTime() - timed));
info("【{}.{}】方法执行返回结果:{}", targetName, methodName, result); info("【{}.{}】方法返回数据打印如下:", targetName, methodName);
printResult(result);
info("************************************【接口调用拦截结束*】************************************"); return result;
} catch (Throwable throwable) {
error("外部接口调用方法【{}.{}】异常:", targetName, methodName, throwable); info("************************************【接口异常拦截结束】*************************************");
throw throwable;
}
} private void printArgs(Object[] args, Method method) {
try { String[] argNames = null;
try {
argNames = ParameterNameUtils.getMethodParamNames(method);
} catch (Exception e) {
error("获取参数名称异常:", e);
} if (args != null) {
for (int i = 0; i < args.length; i++) {
String argName = "";
if (argNames != null && argNames.length >= i) {
argName = argNames[i];
} if (args[i] != null) {
String value = "";
try {
value = mapper.writeValueAsString(args[i]);
} catch (Exception e) {
error("转换参数 \"{}\" 发生异常:", argName, e);
}
info("【参数 \"{}\" 】:({})", argName, value);
} else {
info("参数 \"{}\":NULL", argName);
}
}
}
} catch (Exception e) {
error("【接口调用拦截器】打印方法执行参数异常:", e);
}
} private void printResult(Object result) {
if (result != null) {
try {
info("【返回数据】:({})", mapper.writeValueAsString(result));
} catch (Exception e) {
error("返回数据打印异常:", e);
}
} else {
info("【返回数据】:NULL");
}
} protected final void error(String msg, Object... objects) {
log.error(msg, objects);
} protected final void info(String msg, Object... objects) {
log.info(msg, objects);
}
}

上面使用到了方法参数获取的工具类,代码如下:

 package com.xxx.flipclass.sdk.client.utils;

 import java.io.InputStream;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays; import org.springframework.asm.ClassReader;
import org.springframework.asm.ClassVisitor;
import org.springframework.asm.ClassWriter;
import org.springframework.asm.Label;
import org.springframework.asm.MethodVisitor;
import org.springframework.asm.Opcodes;
import org.springframework.asm.Type; /**
* <b>function:</b> 获取方法参加名称
* @createDate 2016-11-25 下午3:40:33
* @file ParameterNameUtils.java
* @package com.xxx.flipclass.sdk.client.utils
* @project flipclass-sdk-client
* @version 1.0
*/
public abstract class ParameterNameUtils { /**
* 获取指定类指定方法的参数名
*
* @param clazz 要获取参数名的方法所属的类
* @param method 要获取参数名的方法
* @return 按参数顺序排列的参数名列表,如果没有参数,则返回null
*/
public static String[] getMethodParamNames(Class<?> clazz, final Method method) throws Exception { try { final String[] paramNames = new String[method.getParameterTypes().length];
String className = clazz.getName(); int lastDotIndex = className.lastIndexOf(".");
className = className.substring(lastDotIndex + 1) + ".class";
InputStream is = clazz.getResourceAsStream(className); final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
ClassReader cr = new ClassReader(is); cr.accept(new ClassVisitor(Opcodes.ASM4, cw) {
@Override
public MethodVisitor visitMethod(final int access, final String name, final String desc, final String signature, final String[] exceptions) {
final Type[] args = Type.getArgumentTypes(desc);
// 方法名相同并且参数个数相同
if (!name.equals(method.getName()) || !sameType(args, method.getParameterTypes())) {
return super.visitMethod(access, name, desc, signature, exceptions);
}
MethodVisitor v = cv.visitMethod(access, name, desc, signature, exceptions);
return new MethodVisitor(Opcodes.ASM4, v) {
@Override
public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
int i = index - 1;
// 如果是静态方法,则第一就是参数
// 如果不是静态方法,则第一个是"this",然后才是方法的参数
if (Modifier.isStatic(method.getModifiers())) {
i = index;
}
if (i >= 0 && i < paramNames.length) {
paramNames[i] = name;
}
super.visitLocalVariable(name, desc, signature, start, end, index);
} };
}
}, 0);
return paramNames;
} catch (Exception e) {
throw e;
}
} /**
* 比较参数类型是否一致
* @param types asm的类型({@link Type})
* @param clazzes java 类型({@link Class})
* @return
*/
private static boolean sameType(Type[] types, Class<?>[] clazzes) {
// 个数不同
if (types.length != clazzes.length) {
return false;
} for (int i = 0; i < types.length; i++) {
if (!Type.getType(clazzes[i]).equals(types[i])) {
return false;
}
}
return true;
} /**
* 获取方法的参数名
* @param Method
* @return argsNames[]
*/
public static String[] getMethodParamNames(final Method method) throws Exception { return getMethodParamNames(method.getDeclaringClass(), method);
} public static void main(String[] args) throws Exception {
Class<ParameterNameUtils> clazz = ParameterNameUtils.class; Method method = clazz.getDeclaredMethod("getMethodParamNames", Method.class);
String[] parameterNames = ParameterNameUtils.getMethodParamNames(method);
System.out.println(Arrays.toString(parameterNames)); method = clazz.getDeclaredMethod("sameType", Type[].class, Class[].class);
parameterNames = ParameterNameUtils.getMethodParamNames(method);
System.out.println(Arrays.toString(parameterNames));
}
}

最后需要添加配置,拦截哪些接口或是实现类,具体看个人业务

 <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd"> <bean id="externalApiMethodInterceptor" class="com.xxx.flipclass.sdk.framework.aop.ExecutionApiLogMethodInterceptor" /> <aop:config proxy-target-class="true">
<aop:pointcut id="externalApiMethodPointcut" expression="!execution(* com.xxx.flipclass.sdk.client.interfaces..*.loginInfoService.*(..)) and (execution(* com.xxx.*.sdk.client.interfaces..*.*Client*.*(..)) || execution(* com.xxx.*.sdk.client.interfaces..*.*Service*.*(..)))" />
<aop:advisor advice-ref="externalApiMethodInterceptor" pointcut-ref="externalApiMethodPointcut" />
</aop:config>
</beans>

Spring框架下的 “接口调用、MVC请求” 调用参数、返回值、耗时信息输出的更多相关文章

  1. 深入剖析 RabbitMQ —— Spring 框架下实现 AMQP 高级消息队列协议

    前言 消息队列在现今数据量超大,并发量超高的系统中是十分常用的.本文将会对现时最常用到的几款消息队列框架 ActiveMQ.RabbitMQ.Kafka 进行分析对比.详细介绍 RabbitMQ 在 ...

  2. Spring 框架下 (增 删 改 )基本操作

    //applicationContext.xml 配置文件 <?xml version="1.0" encoding="UTF-8"?><be ...

  3. idea插件(mybatis框架下mapper接口快速跳转对应xml文件)亲测好用!

    我相信目前在绝大部分公司里,主要使用的框架是S(spring)S(spring MVC)M(mybatis),其中mybatis总体架构是编写mapper接口,框架扫描其对应的mapper.xml文件 ...

  4. Spring框架下Junit测试

    Spring框架下Junit测试 一.设置 1.1 目录 设置源码目录和测试目录,这样在设置产生测试方法时,会统一放到一个目录,如果没有设置测试目录,则不会产生测试代码. 1.2 增加配置文件 Res ...

  5. atitit.架构设计---方法调用结果使用异常还是返回值

    atitit.架构设计---方法调用结果使用异常还是返回值 1. 应该返回BOOL类型还是异常 1 2. 最终会有四种状况,抛出异常.返回特殊值.阻塞.超时 1 3. 异常的优缺点点 1 4. jav ...

  6. ASP.NET调用存储过程并接收存储过程返回值

    ASP.NET调用存储过程并接收存储过程返回值 2010-08-02 11:26:17|  分类: C#|字号 订阅       2010年02月27日 星期六 23:52 假设表结构Create T ...

  7. springMVC框架下——通用接口之图片上传接口

    我所想要的图片上传接口是指服务器端在完成图片上传后,返回一个可访问的图片地址. spring mvc框架下图片上传非常简单,如下 @RequestMapping(value="/upload ...

  8. 关于Jersey框架下的Aop日志 和Spring 框架下的Aop日志

    摘要 最近新接手的项目经常要查问题,但是,前面一拨人,日志打的非常乱,好多就根本没有打日志,所以弄一个AOP统一打印一下 请求数据和响应数据 框架 spring+springmvc+jersey 正文 ...

  9. Spring框架下的定时任务quartz框架的使用

    手头的这个项目需要用到定时任务,但之前没接触过这东西,所以不太会用,从网上找资料,大致了解了一下,其实也不难.Java的定时任务实现有三种,一种是使用JDK自带的Timer那个类来实现,另一种是使用q ...

随机推荐

  1. sqlite query用法

    本文转自http://blog.csdn.net/double2hao/article/details/50281273,在此感谢作者 query(table, columns, selection, ...

  2. c#开发Mongo笔记第九篇

    用skip略过少量的文档还是不错的.但是要是数量非常多的话,skip就会变得很慢,因为要先找到需要被略过的数据,然后再抛弃这些数据.大多数数据库都会在索引中保存更多的元数据,用于处理skip, 但是m ...

  3. 建立自己的git repository

    环境是windows 1.首先安装Git,下载Git安装包,这个google 就好了 2.注册自己的git账号 https://github.com 3.建立仓库 填好名字 最后那个Initializ ...

  4. singleton注意

    如果singleton里面的构造函数里面对资源进行了初始化,那么程序退出时,需要一个release进行资源释放,并且设置instance = null;

  5. Git命令行初体验

    1. git 版本控制系统 ==============运行环境======== 系统:windows git : Git-1.7.3.1-preview20101002.rar  下载地址:http ...

  6. 一个创建Coco2d-x项目的脚本

    1.使用环境 我测试的环境是Mac OS 10.10 +Coco2d-x 3.2,是使用shell写的脚本,应该linux/unix都应该 可以使用. 2.使用可能出现的问题 使用中可能会爆权限不足的 ...

  7. keil 的头文件 .

    许多初学者使用网上下载的程序时都会遇到这样一个问题,就是头文件找不到.我想就这个问题说明一下./·首先,我们用到的KEIL有几种版本的,头文件也不同.有reg51.h和at89x51.h两种比较常见. ...

  8. jsonp与ajax

    jsonp思维导图:1.定义2.为什么用3.json原理4.优缺点何为跨域?何为JSONP?JSONP技术能实现什么?是否有必要使用JSONP技术? JSON和JSONP??虽然只有一个字母的差别,但 ...

  9. python学习笔记:Day01

    一.python 简介 1.  python是Guido van Rossum在1989年圣诞节期间,为了打发无聊的假期而编写的一个编程语言   2.  pyhton主要应用于数据分析.组件集成.网络 ...

  10. 大熊君说说JS与设计模式之(门面模式Facade)迪米特法则的救赎篇------(监狱的故事)

    一,总体概要 1,笔者浅谈 说起“门面”这个设计模式其实不论新老程序猿都是在无意中就已经运用到此模式了,就像我们美丽的JS程序员一样不经意就使用了闭包处理问题, function Employee(n ...