资讯专栏INFORMATION COLUMN

SpringMVC的Controller拦截日志配置

smartlion / 1966人阅读

摘要:如果处理除了问题,也要返回显示错误的。用上就方便多了,加一个拦截类,加一点配置,就可以搞定所有。其他配置根据情况添加。采用这种方法,实现了对所有方法进行统一的日志记录。

在项目中一直使用SpringMVC做Java后台服务,返回的基本都是JSON。
要求请求参数、返回结果都要打印到log里,方便追踪。
如果处理除了问题,也要返回显示错误的JSON。

如果不用AOP拦截,每个Controller方法都要写成类似如下的样子:

@RequestMapping(value = "/someUrl", produces = { "application/json;charset=UTF-8" })
@ResponseBody
public String someUrl(@ModelAttribute ReqForm reqParams) {
    Logger.info(..., "someUrl request: "+JsonUtil.toJson(reqParams));
    // form validation ...
    try {
        ResultObject result = someService.serviceMethod(reqParams);
        Logger.info(..., "someUrl response: "+JsonUtil.toJson(result));
        return JsonResult.success(result);
    } catch (Exception e) {
        Logger.error(..., "someUrl: "
                + JsonUtil.toJson(reqParams) + " || " + ExceptionUtil.printStackTraceToString(e));
        return JsonResult.fail();
    }
}

很烦,都是重复的东西。用上AOP就方便多了,加一个拦截类,加一点配置,就可以搞定所有。

首先,添加Maven依赖:


  org.aspectj
  aspectjweaver
  1.8.0

然后,实现一个请求拦截处理类(Logger的方法被简化了)

import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
/**
 * 请求拦截处理类
 * 
 * 
 */
public class ControllerInterceptor implements MethodInterceptor {
    @Override
    public String invoke(MethodInvocation invocation) {
        String result = "";
        String paramsStr = "";
        Object value = null;
        Method md = invocation.getMethod();
        try {
            Object[] args = invocation.getArguments();
            paramsStr = this.logRequest(args);
            value = invocation.proceed();
        } catch (Throwable e) {
            if (e instanceof ServiceException) {
                Logger.error(..., ((ServiceException) e).getAlarmId(), md
                        .getDeclaringClass().getSimpleName()
                        + "."
                        + md.getName()
                        + " || "
                        + paramsStr
                        + " || "
                        + printStackTraceToString(e));
            } else {
                Logger.error(..., "some_alarm_id", md
                        .getDeclaringClass().getSimpleName()
                        + "."
                        + md.getName()
                        + " || "
                        + paramsStr
                        + " || "
                        + printStackTraceToString(e));
            }
        }
        if (value != null) {
            result = value.toString();
        } else {
            result = JsonResult.SYSERROR;
        }
        this.logRequestResponse(md, paramsStr, result);
        return result;
    }
    private String logRequest(Object[] args) {
        if (args == null) {
            return "";
        }
        // 请求参数日志信息
        Map params = new HashMap();
        int i = 1;
        for (Object arg : args) {
            if (!(arg instanceof BindingResult) && !(arg instanceof ModelMap) && !(arg instanceof Model)) {
                if (arg instanceof HttpServletRequest) {
                    HttpServletRequest httpRequest = (HttpServletRequest) arg;
                    Enumeration enume = httpRequest.getParameterNames();
                    if (null != enume) {
                        Map hpMap = new HashMap();
                        while (enume.hasMoreElements()) {
                            Object element = enume.nextElement();
                            if (null != element) {
                                String paramName = (String) element;
                                String paramValue = httpRequest.getParameter(paramName);
                                hpMap.put(paramName, paramValue);
                            }
                        }
                        params.put("HttpServletRequest", hpMap);
                    }
                } else {
                    try {
                        params.put("arg" + i, JsonUtil.toJson(arg));
                        i++;
                    } catch (Throwable e) {
                        Logger.warn(..., "CANNOT trasform to json string:"
                                + arg.getClass().getName());
                    }
                }
            }
        }
        String paramsStr = JsonUtil.toJson(params);
        return paramsStr;
    }
    private void logRequestResponse(Method md, String paramsStr, String re) {
        Map logMap = new HashMap();
        logMap.put("controller.method", md.getDeclaringClass().getSimpleName() + "." + md.getName());
        logMap.put("logReq", paramsStr);
        logMap.put("logRes", re);
        Logger.info(..., logMap);
    }
    private String printStackTraceToString(Throwable e) {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        e.printStackTrace(pw);
        return sw.toString().replace("
", " ").replace("
", " ").replace("	", " ");
    }
}

这里用一个类实现了org.aopalliance.intercept.MethodInterceptor接口,实现invoke方法,达到对Controller方法的进入和返回拦截的效果。

注意要在适当的时机执行invocation.proceed()方法,并返回它的返回值。否则所有Controller方法都没返回了。
还有一点,由于我用的SpringMVC,controller方法的一些参数并不需要打印出来,比如BingdingResult ModelMap Model等等。

如果有HttpServletRequest传进来,也不能直接被JSON序列化,需要特殊处理一下。还有其他不能序列化的东西,直接略掉(代码中的try…catch)。

然后,配置下spring。
spring-mvc.xml文件的标签中加入aop相应的schema,并且添加刚才写的拦截类bean。


    
     
           
         
    

上主要添加xmlns:aopxsi:schemaLocation两端aop的配置。其他配置根据情况添加。
切面配置的含义是:在your.controller.package下面所有类所有public的,任何参数,任何返回类型的方法进行拦截。

这样的话,开头的代码就可以简化为:

@RequestMapping(value = "/someUrl", produces = { "application/json;charset=UTF-8" })
@ResponseBody
public String someUrl(@ModelAttribute ReqForm reqParams) {
    // form validation ...
    ResultObject result = someService.serviceMethod(reqParams);
    return JsonResult.success(result);
}

只管业务就可以了,代码清晰了很多。

最后,要注意在web.xml中springMVC的配置,类似于:


    springmvc
    org.springframework.web.servlet.DispatcherServlet
    
        contextConfigLocation
        classpath:/spring/spring-mvc.xml
    
    1


    springmvc
    /*

注意跟业务的bean定义分离开。

采用这种方法,实现了对所有controller方法进行统一的日志记录。
包括进入记录:类名方法名,参数。返回记录:返回值。异常处理返回并记录StackTrace。
同时,使用一个简单的ServiceException可以做到统一扔给Interceptor进行处理。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/65291.html

相关文章

  • SpringMVC【校验器、统一处理异常、RESTful、拦截器】

    摘要:只要有一个拦截器不放行,不能执行完成号不放行和号不放行测试结果总结只有前边的拦截器方法放行,下边的拦截器的才执行。至于他们的拦截器链的调用顺序,和的是没有差别的。 前言 本博文主要讲解的知识点如下: 校验器 统一处理异常 RESTful 拦截器 Validation 在我们的Struts2中,我们是继承ActionSupport来实现校验的...它有两种方式来实现校验的功能 手写...

    marser 评论0 收藏0
  • springboot学习(二)——springmvc配置使用

    摘要:中添加拦截器配置如下拦截所有请求,也就是,只拦截开头的请求。在中并没有提供配置文件的方式来配置拦截器,因此需要使用的代码式配置,配置如下这个属性通常并不需要手动配置,高版本的会自动检测第四点讲下静态资源映射。 以下内容,如有问题,烦请指出,谢谢 上一篇讲解了springboot的helloworld部分,这一篇开始讲解如何使用springboot进行实际的应用开发,基本上寻着sprin...

    hiyayiji 评论0 收藏0
  • 从SpringBoot到SpringMVC

    摘要:概述用久了,深受其约定大于配置的便利性毒害之后,我想回归到时代,看看开发模式中用户是如何参与的。备注当然本文所使用的全是非注解的配置方法,即需要在中进行配置并且需要遵循各种实现原则。而更加通用主流的基于注解的配置方法将在后续文章中详述。 showImg(https://segmentfault.com/img/remote/1460000015244684); 概述 用久了Sprin...

    xavier 评论0 收藏0
  • Spring Boot实践——三种拦截创建

    摘要:中的拦截器在开发中,拦截器是经常用到的功能。该拦截器只能过滤请求,允许多个拦截器同时存在,通过拦截器链管理。当时不再执行后续的拦截器链及被拦截的请求。实现拦截器大致也分为两种,一种是实现接口,另一种利用的注解或配置。 Spring中的拦截器   在web开发中,拦截器是经常用到的功能。它可以帮我们验证是否登陆、权限认证、数据校验、预先设置数据以及统计方法的执行效率等等。今天就来详细的谈...

    fnngj 评论0 收藏0
  • SpringMVC入门笔记

    摘要:简介注解用于修饰的方法,根据的的内容,通过适当的转换为客户端需要格式的数据并且写入到的数据区,从而不通过视图解析器直接将数据响应给客户端。并且这些解析器都实现了接口,在接口中有四个最为主要的接口方法。 SpringMVC 细节方面的东西很多,所以在这里做一篇简单的 SpringMVC 的笔记记录,方便以后查看。 Spring MVC是当前最优秀的MVC框架,自从Spring 2.5版本...

    gekylin 评论0 收藏0

发表评论

0条评论

smartlion

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<