且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

Spring Mvc 公共Controller 使用拦截器注入请求信息

更新时间:2022-02-14 00:48:38

在Spring Mvc 中我们在Controller 中要想使用 HttpServletRequest HttpSession HttpServletResponse 等请求信息,这些信息有多种方式,这里就重点说下我使用的拦截器注入方式

方式一:(使用注解)

/**
     * 每一个Control 被执行时
     * 
     * @author jiangzeyin
     * @date 2016-8-11
     * 
     * @param request
     * @param response
     * @param session
     * @throws IOException
     */
    @ModelAttribute
    public void setReqAndRes(HttpServletRequest request, HttpServletResponse response, HttpSession session) throws IOException {
        this.request = request;
        this.response = response;
        this.session = session;
        this.referer = StringUtil.convertNULL(this.request.getHeader("Referer"));
        this.ip = RequestUtil.getIpAddress(this.request);
        this.response.setCharacterEncoding("UTF-8");
    }

使用@ModelAttribute 注解实现。这个注解有几个作用,如果不了解并且感兴趣的可以去多了解。

方式二(拦截器注入):
第一步:我们需要一个公共拦截器

 @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Class controlClass = ((HandlerMethod) handler).getBean().getClass();
            Object object = handlerMethod.getBean();
            if (AbstractBaseControl.class.isAssignableFrom(controlClass)) {
                abstractBaseControl = (AbstractBaseControl) object;
                abstractBaseControl.setReqAndRes(this.request, this.session, this.response);
            }
        }
        return true;
    }

在拦截器中我们主要判断拦截到的是否是我们的对应Controller 然后调用对应赋值方法。

public void setReqAndRes(HttpServletRequest request, HttpServletResponse response, HttpSession session) throws IOException {
        this.request = request;
        this.response = response;
        this.session = session;
        this.referer = StringUtil.convertNULL(this.request.getHeader("Referer"));
        this.ip = RequestUtil.getIpAddress(this.request);
        this.response.setCharacterEncoding("UTF-8");
    }

setReqAndRes 方法和使用注解方法里面的内容基本相似。

这样我们就基本的对Controller 里面的请求信息可以完全的控制了。在使用这个方法去赋值的时候,在实际业务中我们发现了一个问题。

偶尔request 对象 或者session 对象会报错,因为这两的对象是使用频率最高的。报错最高的对象是request ,这里由于没有记录这个错误现在也无法贴出错误信息。后期看能遇到不,遇到就补上错误信息。

大致情况是这样的。如果使用这样的方式赋值,那么在我们的Controller 的 scope 使用request 时候,这样是完全没有问题的。因为 Controller 的scope 作用域是request 时,那么就代表每一个请求 都会创建一个Controller 对象这样 对象里面的变量就是独立的。
如果我们将Controller 的scope 改变不是request 时候,那么可能出现多个请求使用的同一个Controller 对象 这里对象里面的变量可能在后一个请求把request 对象给覆盖了 这样如果前一个请求的响应耗时大于后一个请求,就意味着前一个请求使用request 对象的时候 就使用到了后一个请求的request 那么这样就出现异常了。

Spring Mvc 公共Controller 使用拦截器注入请求信息
大致是这样的情况。一天我突然想到 因为我们都知道web 程序都是一个线程处理一个请求,我们先在同一个对象中区分不同线程的变量,java 中还存在这个类 那就是 ThreadLocal 这个类就是可以实现在区分不同线程中的不同值。

那么进过改后的BaseControl 去掉以前的那些req res session 等成员变量 改为

 private static ThreadLocal<HttpServletRequest> request_local = new ThreadLocal<>();
 private static ThreadLocal<HttpSession> session_local = new ThreadLocal<>();
 private static ThreadLocal<HttpServletResponse> response_local = new ThreadLocal<>();

同时把 setReqAndRes 方法也得改造 下

 /**
     * 拦截器注入
     *
     * @param request
     * @param session
     * @param response
     */
    public void setReqAndRes(HttpServletRequest request, HttpSession session, HttpServletResponse response) {
        request_local.set(request);
        session_local.set(session);
        response_local.set(response);
        this.ip = RequestUtil.getIpAddress(this.getRequest());
        this.getResponse().setCharacterEncoding("UTF-8");
    }

然后在我们需要对用对应变量时候,就得重新写方法来获取了

    public HttpServletRequest getRequest() {
        return request_local.get();
    }

    public HttpSession getSession() {
        return session_local.get();
    }

    public HttpServletResponse getResponse() {
        return response_local.get();
    }

这样就可以完美的解决了,就可以保证使用的request 等对象都是属于自己的了。

对这个思路欢迎提出质疑和建议:

QQ群: 136715345 加入

邮 件: bwcx_jzy@qq.com 邮件