springmvc在使用@ModelAttribute注解获取Request和Response会产生线程并发不安全问题(转)

  1. springmvc在获取Request和Response有很多方式:具体请看:https://www.cnblogs.com/wade-luffy/p/8867144.html
  2. 产生线程问题的代码如下:
public class BaseController {
    protected HttpServletRequest request;

    protected HttpServletResponse response;

    protected HttpSession session;

    @ModelAttribute
    public void setReqAndRes(HttpServletRequest request, HttpServletResponse response) {

        this.request = request;

        this.response = response;

        this.session = request.getSession();

    }
}

上面的BaseController中使用ModelAttribute注解来获取request和response对象,这样基类继承该类,想要获取对象直接可以拿到,虽然这样很简便,但是这里会产生线程不安全问题,在并发量大的情况下,获取的对象可能是同一个对象,或者为null,这些都是并发造成的问题

分析:

1 . ModelAttribute注解有以下三个作用:
–1)有此注解的方法会在每一个请求前执行,也就是controller执行你请求的方法之前
–2)有此注解的参数,会从前端提交的表单中获取数据(现在一般不使用,因为不使用也可以获取到)
–3)有此注解的方法的返回值,可以直接在spring的view中使用(也可以在BaseContoller中添加方法,传入参数 Model model,spring会自动注入model参数,给这个model添加值,也可在view中使用)

2 . SpringMVC会优先执行被@ModelAttribute注解的方法。也就是说我们每一次请求,都会去调用init()方法,进行初始化操作,那么就会对request,response,httpSession进行赋值操作,一旦赋值,当高并发时,线程问题是必然的

 private void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer container)
            throws Exception {

        while (!this.modelMethods.isEmpty()) {
            InvocableHandlerMethod modelMethod = getNextModelMethod(container).getHandlerMethod();
            ModelAttribute ann = modelMethod.getMethodAnnotation(ModelAttribute.class);
            if (container.containsAttribute(ann.name())) {
                if (!ann.binding()) {
                    container.setBindingDisabled(ann.name());
                }
                continue;
            }

            Object returnValue = modelMethod.invokeForRequest(request, container);
            if (!modelMethod.isVoid()){
                String returnValueName = getNameForReturnValue(returnValue, modelMethod.getReturnType());
                if (!ann.binding()) {
                    container.setBindingDisabled(returnValueName);
                }
                if (!container.containsAttribute(returnValueName)) {
                    container.addAttribute(returnValueName, returnValue);
                }
            }
        }
    }

一, 可以使用其他两种方式来获取request和response对象

1: 使用@Autowired或者@Resource注解注入,

public abstract class BaseController {

    @Autowired
    protected HttpServletRequest request;
     @Autowired
     protected HttpServletResponse response;
}

使用@Autowired或者@Resource注解注入,是线程安全的,当用户发出请求后,会经过FrameworkServlet中的processRequest()方法做了一些骚操作,然后再交给子类DispatcherServlet中的doService()去处理这个请求。这些骚操作就包括把request,response对象包装成ServletRequestAttributes对象,然后放入到ThreadLocal中,看到ThreadLocal就明白了

    private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
            new NamedThreadLocal<RequestAttributes>("Request attributes");

    private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
            new NamedInheritableThreadLocal<RequestAttributes>("Request context");
    private static ServletRequestAttributes currentRequestAttributes() {
        RequestAttributes requestAttr = RequestContextHolder.currentRequestAttributes();
        if (!(requestAttr instanceof ServletRequestAttributes)) {
            throw new IllegalStateException("Current request is not a servlet request");
        }
        return (ServletRequestAttributes) requestAttr;
    }

Autowired注入流程结合上面代码:@Autowired注入HttpServletRequest对象的过程。这里以HttpServletRequest对象注入举例。首先调用DefaultListableBeanFactory中的findAutowireCandidates()方法,判断autowiringType类型是否和requiredType类型一致或者是autowiringType是否是requiredType的父接口(父类)。如果满足条件的话,我们会从resolvableDependencies中通过autowiringType(对应着上文的ServletRequest)拿到autowiringValue(对应着上文的RequestObjectFactory)。然后调用AutowireUtils.resolveAutowiringValue()对我们的ObjectFactory进行处理。

二, 简单粗暴不会产生任何问题:直接在需要request和response对象得方法参数注入即可,有其他参数直接接着在后面写,即可,这个方法参数,作用域在方法,不会产生线程问题

 @RequestMapping(value = "/cms/preview/{id}",method = RequestMethod.GET)

    public void preview(HttpServletResponse response, @PathVariable("id") String id) {

    }

具体详细请看:https://blog.csdn.net/weixin_33768481/article/details/88303335
里面也介绍了进行压力测试模拟高并发请求

解决方案:

现在改成如下代码,则是可以的!

改动:

1  增加了@Autowired

2 设置curUser为private,并且封装成属性:getCurUser()

public class SuperBaseController {
    @Autowired
    protected HttpServletRequest request;
    @Autowired
    protected HttpServletResponse response;
    private SysUser curUser;
    @Value("${isDebug}")
    protected Boolean  isDebug;

    @ModelAttribute
    public void setLang(HttpServletRequest request, HttpServletResponse response) {
        this.sysHostUrl = this.request.getAttribute(ComContants.SYSHOSTURL).toString();
        this.sysHost = this.request.getAttribute(ComContants.SYSHOST).toString();
    }

    public SysUser getCurUser() {
        curUser=WebUtil.getCurrentUser(isDebug, request);
        return curUser;
    }

}

转自1:https://blog.csdn.net/hanjun0612/article/details/103421903

转自2: https://blog.csdn.net/hanjun0612/article/details/103427040

原文地址:https://www.cnblogs.com/q149072205/p/12010394.html

时间: 2024-10-13 04:29:49

springmvc在使用@ModelAttribute注解获取Request和Response会产生线程并发不安全问题(转)的相关文章

在springMVC的controller中获取request,response对象的一个方法

使用springMVC的时候,有些时候会需要获取请求或者响应对象,例如在身份验证的时候,需要获取请求头中的token,在做登录系统的时候需要使用response对象向客户端添加cookie,一个有效的做法是在controller的方法中添加对应参数如下所示: @RestController public class Test2Contrller { @RequestMapping("/test") public void test(HttpServletRequest req, Htt

struts2中获取request、response,与android客户端进行交互(文件传递给客户端)

用struts2作为服务器框架,与android客户端进行交互需要得到request.response对象. struts2中获取request.response有两种方法. 第一种:利用ServletActionContext的静态方法 Struts2 利用ServletActionContext类来维护Servlet对象,ServletActionContext利用ThreadLocal来维护 不同线程的Servlet对象,因此可以使用ServletActionContext类获取,这种方法

java 获取request和response的一种方法

java获取request和response: HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();//获取response  HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttrib

springMVC获取request和response

转载:http://blog.sina.com.cn/s/blog_7085382f0102v9jg.html 1.参数 例如: @RequestMapping("/test") @ResponseBody public void saveTest(HttpServletRequest req, HttpServletResponse resp){ } 这样的话如果要用ajax每次都要将response作为参数传递,不方便使用 2.加入监听器,然后再controller里面获取 (1)

Struts2,springMVC获取request和response

springMVC获取request和response1:在BaseController中加入: protected HttpServletRequest request;   protected HttpServletResponse response;   protected HttpSession session; @ModelAttribute   public void setReqAndRes(HttpServletRequest request, HttpServletRespon

springMVC 中几种获取request和response的方式

1.最简单方式:参数 例如: @RequestMapping("/test") @ResponseBody public void saveTest(HttpServletRequest req, HttpServletResponse resp){ } 2.加入监听器,然后在代码里面获取 HttpServletRequest req = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).ge

Spring中如何获取request的方法汇总及其线程安全性分析

前言 本文将介绍在Spring MVC开发的web系统中,获取request对象的几种方法,并讨论其线程安全性.下面话不多说了,来一起看看详细的介绍吧. 概述 在使用Spring MVC开发Web系统时,经常需要在处理请求时使用request对象,比如获取客户端ip地址.请求的url.header中的属性(如cookie.授权信息).body中的数据等.由于在Spring MVC中,处理请求的Controller.Service等对象都是单例的,因此获取request对象时最需要注意的问题,便是

ssm框架中获取request和response

有时候需要在Controller.service中使用HttpServletRequest或HttpServletResponse对象,这里记下3种方法 1.直接在Controller接收参数HttpServletRequest或HttpServletResponse,再传到service类中 2.在service类中需要使用req和res对象的方法上加@ModelAttribute注解,并且在方法参数列表里接收这两个对象即可在方法里使用 3.在web.xml中配置监听器(专门监听http对象)

springmvc 下使用自定义注解获取登录信息

大家的项目中在controller层是怎样获取登录用户的信息呢? User loginUser=LoginUtil.getLoginUser(); 我想有些同学是通过这样获得的,如果这样实现的话, 恭喜你,你的Controller层已经丧失了单元测试的能力. 因为执行这个controller所需要的参数并没有完全通过参数列表来获得,而依赖于全局环境(web环境) 如果这样做使得springmvc为环境解耦所设计的整个架构变得毫无意义. 那怎么样写才算是高大上呢?先贴出最后效果 @RequestM