请选择 进入手机版 | 继续访问电脑版

技术控

    今日:0| 主题:61300
收藏本版 (1)
最新软件应用技术尽在掌握

[其他] Spring框架下的 “接口调用、MVC请求” 调用参数、返回值、耗时信息输出 ...

[复制链接]
火包友 发表于 2016-11-29 10:40:12
421 2
主要拦截前端或后天的请求,打印请求方法参数、返回值、耗时、异常的日志。方便开发调试,能很快定位到问题出现在哪个方法中。
   

Spring框架下的 “接口调用、MVC请求” 调用参数、返回值、耗时信息输出 ...

Spring框架下的 “接口调用、MVC请求” 调用参数、返回值、耗时信息输出 ...-1-技术控-function,package,spring,import,拦截器

   

Spring框架下的 “接口调用、MVC请求” 调用参数、返回值、耗时信息输出 ...

Spring框架下的 “接口调用、MVC请求” 调用参数、返回值、耗时信息输出 ...-2-技术控-function,package,spring,import,拦截器

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


  1.   1 import java.util.Date;
  2.   2 import java.util.Iterator;
  3.   3 import java.util.Map;
  4.   4 import java.util.Set;
  5.   5
  6.   6 import javax.servlet.http.HttpServletRequest;
  7.   7 import javax.servlet.http.HttpServletResponse;
  8.   8
  9.   9 import org.codehaus.jackson.map.ObjectMapper;
  10. 10 import org.springframework.core.NamedThreadLocal;
  11. 11 import org.springframework.web.servlet.HandlerInterceptor;
  12. 12 import org.springframework.web.servlet.ModelAndView;
  13. 13
  14. 14 import com.xxx.eduyun.sdk.log.ApplicationLogging;
  15. 15 import com.xxx.flipclass.sdk.client.utils.TimeUtil;
  16. 16
  17. 17 /**
  18. 18  * <b>function:</b> spring mvc 请求拦截器
  19. 19  * @author hoojo
  20. 20  * @createDate 2016-11-24 下午3:19:27
  21. 21  * @file MVCRequestInterceptor.java
  22. 22  * @package com.xxx.eduyun.app.mvc.interceptor
  23. 23  * @project eduyun-app-web
  24. 24  * @blog http://blog.csdn.net/IBM_hoojo
  25. 25  * @email [email protected]
  26. 26  * @version 1.0
  27. 27  */
  28. 28 public class MVCRequestInterceptor extends ApplicationLogging implements HandlerInterceptor {
  29. 29
  30. 30     private static final ObjectMapper mapper = new ObjectMapper();
  31. 31     private NamedThreadLocal<Long>  startTimeThreadLocal = new NamedThreadLocal<Long>("StopWatch-startTimed");  
  32. 32     
  33. 33     @Override
  34. 34     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  35. 35         
  36. 36         
  37. 37         info("##############################【一个MVC完整请求开始】##############################");
  38. 38         
  39. 39         info("*******************MVC业务处理开始**********************");
  40. 40         try {
  41. 41             long timed = System.currentTimeMillis();
  42. 42             startTimeThreadLocal.set(timed);
  43. 43            
  44. 44             String requestURL = request.getRequestURI();
  45. 45             info("当前请求的URL:【{}】", requestURL);
  46. 46             info("执行目标方法: {}", handler);
  47. 47            
  48. 48             Map<String, ?> params = request.getParameterMap();
  49. 49             if (!params.isEmpty()) {
  50. 50                 info("当前请求参数打印:");
  51. 51                 print(request.getParameterMap(), "参数");
  52. 52             }
  53. 53         } catch (Exception e) {
  54. 54             error("MVC业务处理-拦截器异常:", e);
  55. 55         }
  56. 56         info("*******************MVC业务处理结束**********************");
  57. 57         
  58. 58         return true;
  59. 59     }
  60. 60
  61. 61     @Override
  62. 62     public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
  63. 63         
  64. 64         info("*******************一个MVC 视图渲染开始**********************");
  65. 65         
  66. 66         try {
  67. 67             info("执行业务逻辑代码耗时:【{}】", TimeUtil.formatTime(new Date().getTime() - startTimeThreadLocal.get()));
  68. 68             String requestURL = request.getRequestURI();
  69. 69             info("当前请求的URL:【{}】", requestURL);
  70. 70            
  71. 71             if (modelAndView != null) {
  72. 72                 info("即将返回到MVC视图:{}", modelAndView.getViewName());
  73. 73                 
  74. 74                 if (modelAndView.getView() != null) {
  75. 75                     info("返回到MVC视图内容类型ContentType:{}", modelAndView.getView().getContentType());
  76. 76                 }
  77. 77                 
  78. 78                 if (!modelAndView.getModel().isEmpty()) {
  79. 79                     
  80. 80                     info("返回到MVC视图{}数据打印如下:", modelAndView.getViewName());
  81. 81                     print(modelAndView.getModel(), "返回数据");
  82. 82                 }
  83. 83             }
  84. 84         } catch (Exception e) {
  85. 85             error("MVC 视图渲染-拦截器异常:", e);
  86. 86         }
  87. 87         
  88. 88         info("*******************一个MVC 视图渲染结束**********************");
  89. 89     }
  90. 90
  91. 91     @Override
  92. 92     public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
  93. 93         
  94. 94         try {
  95. 95             String requestURL = request.getRequestURI();
  96. 96             info("MVC返回请求完成URL:【{}】", requestURL);
  97. 97             info("MVC返回请求完成耗时:【{}】", TimeUtil.formatTime(new Date().getTime() - startTimeThreadLocal.get()));
  98. 98             if (ex != null) {
  99. 99                 info("MVC返回请求发生异常:", ex.getMessage());
  100. 100                 error("异常信息如下:", ex);
  101. 101             }
  102. 102         } catch (Exception e) {
  103. 103             error("MVC完成返回-拦截器异常:", e);
  104. 104         }
  105. 105         
  106. 106         info("##############################【一个MVC完整请求完成】##############################");
  107. 107     }
  108. 108     
  109. 109     private void print(Map<String, ?> map, String prefix) {
  110. 110         if (map != null) {
  111. 111             Set<String> keys = map.keySet();
  112. 112             Iterator<String> iter = keys.iterator();
  113. 113             while (iter.hasNext()) {
  114. 114                 
  115. 115                 String name = iter.next();
  116. 116                 if (name.contains("org.springframework.validation.BindingResult")) {
  117. 117                     continue;
  118. 118                 }
  119. 119                 
  120. 120                 String value = "";
  121. 121                 try {
  122. 122                     value = mapper.writeValueAsString(map.get(name));
  123. 123                 } catch (Exception e) {
  124. 124                     error("转换参数【{}】发生异常:", name, e);
  125. 125                 }
  126. 126                 info("{} "{}": {}", prefix, name, value);
  127. 127             }
  128. 128         }
  129. 129     }
  130. 130 }

  131. View Code
复制代码
spring-mvc.xml增加配置内容


  1. 1 <mvc:interceptors>
  2. 2     <mvc:interceptor>
  3. 3         <mvc:mapping path="/**"/>
  4. 4         <mvc:exclude-mapping path="/exceltemplate/**" />
  5. 5         <mvc:exclude-mapping path="/statics/**" />
  6. 6         <mvc:exclude-mapping path="/global/**" />
  7. 7         <mvc:exclude-mapping path="/denied/**" />
  8. 8         <mvc:exclude-mapping path="/favicon.ico" />
  9. 9         <mvc:exclude-mapping path="/index.jsp" />
  10. 10         <bean class="com.xxx.eduyun.app.mvc.interceptor.MVCRequestInterceptor"/>
  11. 11     </mvc:interceptor>
  12. 12 </mvc:interceptors>

  13. View Code
复制代码
过滤静态资源,一些静态资源不需要拦截,在这里配置黑名单不让它进入拦截器。
  下面是sdk接口拦截器,用到spirng的aop的MethodIntercept


  1.   1 package com.xxx.flipclass.sdk.framework.aop;
  2.   2
  3.   3 import java.lang.reflect.Method;
  4.   4 import java.util.Date;
  5.   5
  6.   6 import org.aopalliance.intercept.MethodInterceptor;
  7.   7 import org.aopalliance.intercept.MethodInvocation;
  8.   8 import org.apache.logging.log4j.LogManager;
  9.   9 import org.apache.logging.log4j.Logger;
  10. 10 import org.codehaus.jackson.map.ObjectMapper;
  11. 11
  12. 12 import com.xxx.flipclass.sdk.client.utils.ParameterNameUtils;
  13. 13 import com.xxx.flipclass.sdk.client.utils.TimeUtil;
  14. 14
  15. 15 /**
  16. 16  * <b>function:</b> Spring 接口调用拦截器,主要拦截com.xxx.*.sdk.client对外接口
  17. 17  * @author hoojo
  18. 18  * @createDate 2016-11-24 下午5:39:57
  19. 19  * @file ExecutionApiLogMethodInterceptor.java
  20. 20  * @package com.xxx.eduyun.sdk.framework
  21. 21  * @project eduyun-sdk-service
  22. 22  * @blog http://blog.csdn.net/IBM_hoojo
  23. 23  * @email [email protected]
  24. 24  * @version 1.0
  25. 25  */
  26. 26 public class ExecutionApiLogMethodInterceptor implements MethodInterceptor {
  27. 27
  28. 28     private Logger log = LogManager.getLogger(ExecutionApiLogMethodInterceptor.class);
  29. 29     private static final ObjectMapper mapper = new ObjectMapper();
  30. 30     
  31. 31     @Override
  32. 32     public Object invoke(MethodInvocation invocation) throws Throwable {
  33. 33
  34. 34         info("************************************【接口调用拦截开始】*************************************");
  35. 35         String targetName = invocation.getThis().getClass().getSimpleName();
  36. 36         Method method = invocation.getMethod();
  37. 37         String methodName = method.getName();
  38. 38         
  39. 39         info("系统开始执行方法:{}.{}", targetName, methodName);
  40. 40         
  41. 41         info("【{}.{}】方法参数打印如下:", targetName, methodName);
  42. 42         Object[] args = invocation.getArguments();
  43. 43         
  44. 44         //printArgs(args, method, invocation.getThis().getClass());
  45. 45         printArgs(args, method);
  46. 46         
  47. 47         try {
  48. 48             long timed = System.currentTimeMillis();
  49. 49            
  50. 50             Object result = invocation.proceed();
  51. 51
  52. 52             info("【{}.{}】方法执行完成,耗时:【{}】", targetName, methodName, TimeUtil.formatTime(new Date().getTime() - timed));
  53. 53             info("【{}.{}】方法执行返回结果:{}", targetName, methodName, result);
  54. 54            
  55. 55             info("【{}.{}】方法返回数据打印如下:", targetName, methodName);
  56. 56             printResult(result);
  57. 57             info("************************************【接口调用拦截结束*】************************************");
  58. 58            
  59. 59             return result;
  60. 60         } catch (Throwable throwable) {
  61. 61             error("外部接口调用方法【{}.{}】异常:", targetName, methodName, throwable);
  62. 62            
  63. 63             info("************************************【接口异常拦截结束】*************************************");
  64. 64             throw throwable;
  65. 65         }
  66. 66     }
  67. 67     
  68. 68     private void printArgs(Object[] args, Method method) {
  69. 69         try {
  70. 70            
  71. 71             String[] argNames = null;
  72. 72             try {
  73. 73                 argNames = ParameterNameUtils.getMethodParamNames(method);
  74. 74             } catch (Exception e) {
  75. 75                 error("获取参数名称异常:", e);
  76. 76             }
  77. 77            
  78. 78             if (args != null) {
  79. 79                 for (int i = 0; i < args.length; i++) {
  80. 80                     String argName = "";
  81. 81                     if (argNames != null && argNames.length >= i) {
  82. 82                         argName = argNames[i];
  83. 83                     }
  84. 84                     
  85. 85                     if (args[i] != null) {
  86. 86                         String value = "";
  87. 87                         try {
  88. 88                             value = mapper.writeValueAsString(args[i]);
  89. 89                         } catch (Exception e) {
  90. 90                             error("转换参数 "{}" 发生异常:", argName, e);
  91. 91                         }
  92. 92                         info("【参数 "{}" 】:({})", argName, value);
  93. 93                     } else {
  94. 94                         info("参数 "{}":NULL", argName);
  95. 95                     }
  96. 96                 }
  97. 97             }
  98. 98         } catch (Exception e) {
  99. 99             error("【接口调用拦截器】打印方法执行参数异常:", e);
  100. 100         }
  101. 101     }
  102. 102     
  103. 103     private void printResult(Object result) {
  104. 104         if (result != null) {
  105. 105             try {
  106. 106                 info("【返回数据】:({})", mapper.writeValueAsString(result));
  107. 107             } catch (Exception e) {
  108. 108                 error("返回数据打印异常:", e);
  109. 109             }
  110. 110         } else {
  111. 111             info("【返回数据】:NULL");
  112. 112         }
  113. 113     }
  114. 114     
  115. 115     protected final void error(String msg, Object... objects) {
  116. 116         log.error(msg, objects);
  117. 117     }
  118. 118
  119. 119     protected final void info(String msg, Object... objects) {
  120. 120         log.info(msg, objects);
  121. 121     }
  122. 122 }

  123. View Code
复制代码
上面使用到了方法参数获取的工具类,代码如下:


  1.   1 package com.xxx.flipclass.sdk.client.utils;
  2.   2
  3.   3 import java.io.InputStream;
  4.   4 import java.lang.reflect.Method;
  5.   5 import java.lang.reflect.Modifier;
  6.   6 import java.util.Arrays;
  7.   7
  8.   8 import org.springframework.asm.ClassReader;
  9.   9 import org.springframework.asm.ClassVisitor;
  10. 10 import org.springframework.asm.ClassWriter;
  11. 11 import org.springframework.asm.Label;
  12. 12 import org.springframework.asm.MethodVisitor;
  13. 13 import org.springframework.asm.Opcodes;
  14. 14 import org.springframework.asm.Type;
  15. 15
  16. 16 /**
  17. 17  * <b>function:</b> 获取方法参加名称
  18. 18  * @createDate 2016-11-25 下午3:40:33
  19. 19  * @file ParameterNameUtils.java
  20. 20  * @package com.xxx.flipclass.sdk.client.utils
  21. 21  * @project flipclass-sdk-client
  22. 22  * @version 1.0
  23. 23  */
  24. 24 public abstract class ParameterNameUtils {
  25. 25
  26. 26     /**
  27. 27      * 获取指定类指定方法的参数名
  28. 28      *
  29. 29      * @param clazz 要获取参数名的方法所属的类
  30. 30      * @param method 要获取参数名的方法
  31. 31      * @return 按参数顺序排列的参数名列表,如果没有参数,则返回null
  32. 32      */
  33. 33     public static String[] getMethodParamNames(Class<?> clazz, final Method method) throws Exception {
  34. 34         
  35. 35         try {
  36. 36            
  37. 37             final String[] paramNames = new String[method.getParameterTypes().length];
  38. 38             String className = clazz.getName();
  39. 39            
  40. 40             int lastDotIndex = className.lastIndexOf(".");
  41. 41             className = className.substring(lastDotIndex + 1) + ".class";
  42. 42             InputStream is = clazz.getResourceAsStream(className);
  43. 43            
  44. 44             final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
  45. 45             ClassReader cr = new ClassReader(is);
  46. 46            
  47. 47             cr.accept(new ClassVisitor(Opcodes.ASM4, cw) {
  48. 48                 @Override
  49. 49                 public MethodVisitor visitMethod(final int access, final String name, final String desc, final String signature, final String[] exceptions) {
  50. 50                     final Type[] args = Type.getArgumentTypes(desc);
  51. 51                     // 方法名相同并且参数个数相同
  52. 52                     if (!name.equals(method.getName()) || !sameType(args, method.getParameterTypes())) {
  53. 53                         return super.visitMethod(access, name, desc, signature, exceptions);
  54. 54                     }
  55. 55                     MethodVisitor v = cv.visitMethod(access, name, desc, signature, exceptions);
  56. 56                     return new MethodVisitor(Opcodes.ASM4, v) {
  57. 57                         @Override
  58. 58                         public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
  59. 59                             int i = index - 1;
  60. 60                             // 如果是静态方法,则第一就是参数
  61. 61                             // 如果不是静态方法,则第一个是"this",然后才是方法的参数
  62. 62                             if (Modifier.isStatic(method.getModifiers())) {
  63. 63                                 i = index;
  64. 64                             }
  65. 65                             if (i >= 0 && i < paramNames.length) {
  66. 66                                 paramNames[i] = name;
  67. 67                             }
  68. 68                             super.visitLocalVariable(name, desc, signature, start, end, index);
  69. 69                         }
  70. 70                        
  71. 71                     };
  72. 72                 }
  73. 73             }, 0);
  74. 74             return paramNames;
  75. 75         } catch (Exception e) {
  76. 76             throw e;
  77. 77         }
  78. 78     }
  79. 79     
  80. 80     /**
  81. 81      * 比较参数类型是否一致
  82. 82      * @param types asm的类型([email protected] Type})
  83. 83      * @param clazzes java 类型([email protected] Class})
  84. 84      * @return
  85. 85      */
  86. 86     private static boolean sameType(Type[] types, Class<?>[] clazzes) {
  87. 87         // 个数不同
  88. 88         if (types.length != clazzes.length) {
  89. 89             return false;
  90. 90         }
  91. 91
  92. 92         for (int i = 0; i < types.length; i++) {
  93. 93             if (!Type.getType(clazzes[i]).equals(types[i])) {
  94. 94                 return false;
  95. 95             }
  96. 96         }
  97. 97         return true;
  98. 98     }
  99. 99
  100. 100     /**
  101. 101      * 获取方法的参数名
  102. 102      * @param Method
  103. 103      * @return argsNames[]
  104. 104      */
  105. 105     public static String[] getMethodParamNames(final Method method) throws Exception {
  106. 106         
  107. 107         return getMethodParamNames(method.getDeclaringClass(), method);
  108. 108     }
  109. 109
  110. 110     public static void main(String[] args) throws Exception {
  111. 111         Class<ParameterNameUtils> clazz = ParameterNameUtils.class;
  112. 112         
  113. 113         Method method = clazz.getDeclaredMethod("getMethodParamNames", Method.class);
  114. 114         String[] parameterNames = ParameterNameUtils.getMethodParamNames(method);
  115. 115         System.out.println(Arrays.toString(parameterNames));
  116. 116         
  117. 117         method = clazz.getDeclaredMethod("sameType", Type[].class, Class[].class);
  118. 118         parameterNames = ParameterNameUtils.getMethodParamNames(method);
  119. 119         System.out.println(Arrays.toString(parameterNames));
  120. 120     }
  121. 121 }

  122. View Code
复制代码
最后需要添加配置,拦截哪些接口或是实现类,具体看个人业务


  1. 1 <?xml version="1.0" encoding="UTF-8"?>
  2. 2 <beans xmlns="http://www.springframework.org/schema/beans"
  3. 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. 4     xmlns:aop="http://www.springframework.org/schema/aop"
  5. 5     xsi:schemaLocation="http://www.springframework.org/schema/beans
  6. 6                         http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
  7. 7                         http://www.springframework.org/schema/aop
  8. 8                          http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">
  9. 9                        
  10. 10
  11. 11     <bean id="externalApiMethodInterceptor" class="com.xxx.flipclass.sdk.framework.aop.ExecutionApiLogMethodInterceptor" />
  12. 12
  13. 13     <aop:config proxy-target-class="true">
  14. 14         <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*.*(..)))" />
  15. 15         <aop:advisor advice-ref="externalApiMethodInterceptor" pointcut-ref="externalApiMethodPointcut" />
  16. 16     </aop:config>
  17. 17 </beans>

  18. View Code
复制代码
523694 发表于 2016-11-29 11:01:41
楼主,约么?
回复 支持 反对

使用道具 举报

董小磊 发表于 2016-11-29 11:51:18
太高深了,理解力不够用了!
回复 支持 反对

使用道具 举报

我要投稿

推荐阅读


回页顶回复上一篇下一篇回列表
手机版/c.CoLaBug.com ( 粤ICP备05003221号 | 文网文[2010]257号 | 粤公网安备 44010402000842号 )

© 2001-2017 Comsenz Inc.

返回顶部 返回列表