当使用RESTFUL风格,表达的方法为非GET以及POST时,需要使用HiddenHttpMethodFilter来对隐藏的方法进行解析。通常就是直接调用getParameter方法解析出隐藏域来值,然后传给Controller就行了。
但是,当表单中有文件上传时,表单实际上变为了一个输入流,需要进过特殊处理(解析)后,才能被HiddenHttpMethodFilter所识别,然后确定方法类型。而解析输入流的东西,就是CommonsMultipartResolver。
使用SpringMVC时,通常在配置文件中配置CommonsMultipartResolver为一个Bean,然后它完成对Form的解析,解析出普通的上传域以及MultipartFile类型的上传域(也就是file),自动绑定到相应的类中。
如果在Form中,隐藏了name="_method" value="PUT/DELETE/..."这个域,我们原本希望HiddenHttpMethodFilter来解析为指定的方法,但是,此时必须先经过CommonsMultipartResolver解析出相应的域后,HiddeHttpMethodFilter才能找到name="_method"这个隐藏域,进行处理。
SpringMVC的执行流程中,过滤器起作用,比如HiddenHttpMethodFilter过滤器配置的拦截路径为所有的请求,那么它会在请求发送到DispatcherServlet的时候,就进行解析。但是没有经过CommonsMultipartResolver的解析(它是在DisptacherServlet通过HandlerAdapter来调用相应Handler的过程中,进行参数的解析绑定时才起作用的),此时HidedenHttpMethodFilter解析不出来什么鸟东西,请求的方法还是为post方法,就会找不到相应的Handler。这就像是死锁。。。
怎么办呢?我们可以在HiddenHttpMethodFilter之前,再配置一个Filter,先把参数解析了不就好了嘛。
于是乎就配置一个MultipartFilter这个过滤器,指定一个初始化参数:multipartResolverBeanName,它的值为我们在spirng配置文件中配置好的CommonsMultipartResolver这个Bean的id就好了。
然而并非如此。因为在MultipartFilter进行过滤的时候,调用的Bean,是在Spring的根容器中配置的Bean。我们配置的普通的Spirng配置文件,只是DispatcherServlet的配置文件,并不是根容器的配置文件。(其实DispatcherServlet只是一个普通Servlet,我们可以简单的认为它也有一个自己的上下文环境,起着Spring容器的作用)。
这里简单说一下,上下文环境(context),有ServletContext、DispatcherServlet的上下文、以及spring根上下文webApplicationContext。在web项目中,ServletContext包含着webApplicationContext以及Dispatcher的Context,同时,web Application Context也是Dispatcher的context的父容器。
spring在执行ApplicationContext的个体Bean时,如果在自己的context上下文中找不到Bean,就会到父容器中去找。但是父容器不会找子容器。
所以,我们需要把CommonsMultipartResolver配置在WebApplicationContext中。单独写一个配置文件:root-context.xml:
-
<!--配置文件上传解析器-->
-
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
-
<!--指定文件上传的总大小不超过20M-->
-
<property name="maxUploadSize" value="20000000"/>
-
<property name="defaultEncoding" value="utf-8"/>
-
</bean>
在web.xml中,配置ServletContext的初始化参数:contextConfigLocation,仔配置一个监听器:ContextLoadLinstener。
-
<context-param>
-
<param-name>contextConfigLocation
</param-name>
-
<param-value>classpath:spring/root-context.xml
</param-value>
-
</context-param>
-
-
<listener>
-
<listener-class>org.springframework.web.context.ContextLoaderListener
</listener-class>
-
</listener>
-
-
-
<filter>
-
<filter-name>MultipartFilter
</filter-name>
-
<filter-class>org.springframework.web.multipart.support.MultipartFilter
</filter-class>
-
<init-param>
-
<param-name>multipartResolverBeanName
</param-name>
-
<param-value>multipartResolver
</param-value>
-
</init-param>
-
</filter>
-
<filter-mapping>
-
<filter-name>MultipartFilter
</filter-name>
-
<url-pattern>/*
</url-pattern>
-
</filter-mapping>
-
-
<filter>
-
<filter-name>hiddenHttpMethodFilter
</filter-name>
-
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter
</filter-class>
-
</filter>
-
<filter-mapping>
-
<filter-name>hiddenHttpMethodFilter
</filter-name>
-
<url-pattern>/*
</url-pattern>
-
</filter-mapping>
-
-
<servlet>
-
<servlet-name>springMVC
</servlet-name>
-
<servlet-class>org.springframework.web.servlet.DispatcherServlet
</servlet-class>
-
<init-param>
-
<param-name>contextConfigLocation
</param-name>
-
<param-value>classpath:spring/applicationContext.xml
</param-value>
-
</init-param>
-
</servlet>
-
<servlet-mapping>
-
<servlet-name>springMVC
</servlet-name>
-
<url-pattern>/
</url-pattern>
-
</servlet-mapping>
当Servlet容器加载的时候,容器会读web.xml中的listener元素以及context-param元素。
紧接着,容器创建ServletContext,整个web项目通用。然后容器根据读取的listener以及初始化参数,进行相应的处理。
如此,就解决了问题。
<li class="tool-item tool-active is-like tool-clicked"><a href="javascript:;"><svg class="icon" aria-hidden="true"> <use xlink:href="#csdnc-thumbsup"></use> </svg><span class="name">点赞</span> <span class="count">1</span> </a></li> <li class="tool-item tool-active is-collection "><a href="javascript:;" data-report-click="{"mod":"popu_824"}"><svg class="icon" aria-hidden="true"> <use xlink:href="#icon-csdnc-Collection-G"></use> </svg><span class="name">收藏</span></a></li> <li class="tool-item tool-active is-share"><a href="javascript:;"><svg class="icon" aria-hidden="true"> <use xlink:href="#icon-csdnc-fenxiang"></use> </svg>分享</a></li> <!--打赏开始--> <!--打赏结束--> <li class="tool-item tool-more"> <a> <svg t="1575545411852" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5717" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M179.176 499.222m-113.245 0a113.245 113.245 0 1 0 226.49 0 113.245 113.245 0 1 0-226.49 0Z" p-id="5718"></path><path d="M509.684 499.222m-113.245 0a113.245 113.245 0 1 0 226.49 0 113.245 113.245 0 1 0-226.49 0Z" p-id="5719"></path><path d="M846.175 499.222m-113.245 0a113.245 113.245 0 1 0 226.49 0 113.245 113.245 0 1 0-226.49 0Z" p-id="5720"></path></svg> </a> <ul class="more-box"> <li class="item"><a class="article-report">文章举报</a></li> </ul> </li> </ul> </div> </div> <div class="person-messagebox"> <div class="left-message"><a href="https://blog.csdn.net/WARGON"> <img src="https://profile.csdnimg.cn/C/A/D/3_wargon" class="avatar_pic" username="WARGON"> <img src="https://g.csdnimg.cn/static/user-reg-year/1x/2.png" class="user-years"> </a></div> <div class="middle-message"> <div class="title"><span class="tit"><a href="https://blog.csdn.net/WARGON" data-report-click="{"mod":"popu_379"}" target="_blank">WARGON</a></span> </div> <div class="text"><span>发布了120 篇原创文章</span> · <span>获赞 16</span> · <span>访问量 7万+</span></div> </div> <div class="right-message"> <a href="https://im.csdn.net/im/main.html?userName=WARGON" target="_blank" class="btn btn-sm btn-red-hollow bt-button personal-letter">私信 </a> <a class="btn btn-sm bt-button personal-watch" data-report-click="{"mod":"popu_379"}">关注</a> </div> </div> </div> 转载地址:https://blog.csdn.net/wargon/article/details/81811520