美文网首页
SpringMVC线程安全问题和@ModelAttribute注

SpringMVC线程安全问题和@ModelAttribute注

作者: 雨夏_ | 来源:发表于2019-01-25 11:42 被阅读176次

首先,我使用了Spring,意味着对象都是通过IOC容器进行管理。容器会帮我们创建和维护对象,但是为了提高性能,这些对象都是单例的,当然,你也可以通过设置scope改变为多例。

所以我一般不能在Spring管理的对象中使用成员变量,那样会出现线程安全问题。

@ModelAttribute是SpringMVC中一个常用的注解,使用它注释的方法会在Controller方法执行前执行

protected ModelAndView invokeHandlerMethod(HttpServletRequest request, 
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
       ......
       //执行@ModelAttribute注解的方法
       modelFactory.initModel(webRequest, mavContainer, invocableMethod);
       ......
       //执行Controller中的方法
       invocableMethod.invokeAndHandle(webRequest, mavContainer, new Object[0]);
}

网上有这样一种错误的用法:

public class BaseController {
    private HttpServletRequest request;
    private HttpServletResponse response;
    @ModelAttribute
    public void setReqAndRes(HttpServletRequest request,
            HttpServletResponse response) {
        this.request = request;
        this.response = response;
    }
}

这样虽然能让我们不用每次都注入一些常用的对象,但是这样的做法会出现线程安全问题,千万别这样用。除非你设置为多例,但是性能会下降。
不过这里值得一提的是,如果你将request用@resource或者@Autowired注解注入,是不会出现线程安全问题的。

引用网友小杨vita的解释

  1. 在controller中注入的request是jdk动态代理对象,ObjectFactoryDelegatingInvocationHandler的实例.当我们调用成员域request的方法的时候其实是调用了objectFactory的getObject()对象的相关方法.这里的objectFactory是RequestObjectFactory.
  2. RequestObjectFactory的getObject其实是从RequestContextHolder的threadlocal中去取值的.
  3. 请求刚进入springmvc的dispatcherServlet的时候会把request相关对象设置到RequestContextHolder的threadlocal中去.

我们可以发现,SpringMVC是通过ThreadLocal对象来保证线程安全的。那么我们是不是也可以这样做?于是上面线程不安全代码可以改为如下内容:

public class BaseController {
    private static final ThreadLocal<HttpServletRequest> requestContainer = new ThreadLocal<>();
    private static final ThreadLocal<HttpServletResponse> responseContainer = new ThreadLocal<>();
    @ModelAttribute
    public void setReqAndRes(HttpServletRequest request,
            HttpServletResponse response) {
        requestContainer.set(request);
        responseContainer.set(response);
    }
    public HttpServletRequest getRequest() {
        return requestContainer.get();
    }
    public HttpServletResponse getResponse() {
        return responseContainer.get();
    }
}

相关文章

网友评论

      本文标题:SpringMVC线程安全问题和@ModelAttribute注

      本文链接:https://www.haomeiwen.com/subject/avcmjqtx.html