• 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

  • 相关阅读:
    Python基础系列----语法、数据类型、变量、编码
    Python基础系列----环境的搭建及简单输入、输出
    Python 从基础------进阶------算法 系列
    Python数据库访问公共组件及模拟Http请求
    急!急!急!请问win32api参数乱码如何解决!
    打印之Lodop
    Elasticsearch 6.7.2 源码编译
    ElasticSearch源码之——Gateway
    ElasticSearch源码之——Netty在Elasticsearch中的应用
    从BIO到Netty
  • 原文地址:https://www.cnblogs.com/q149072205/p/12010394.html
Copyright © 2020-2023  润新知