前言
在这篇博客中,我们将深入探讨Spring MVC的请求处理机制,包括请求的接收、处理和响应的全过程。我们将通过示例代码,帮助读者理解如何使用Spring MVC来处理HTTP请求,希望通过本篇博客,读者能够掌握Spring MVC的基本用法,并能够在自己的项目中灵活应用。
请求数据的接收
接收Restful风格数据
什么是Rest风格?
Rest(Representational State Transfer)表象化状态转变(表述性状态转变),在2000年被提出,基于HTTP、URI、xml、JSON等标准和协议,支持轻量级、跨平台、跨语言的架构设计。是Web服务的一种新网络应用程序的设计风格和开发方式。
Restful风格的请求,常见的规则有如下三点:
- 用URI表示某个模块资源,资源名称为名词;

- 用请求方式表示模块具体业务动作,例如:GET表示查询、POST表示插入、PUT表示更新、DELETE表示删除

- 用HTTP响应状态码表示结果,国内常用的响应包括三部分:状态码、状态信息、响应数据

接收Restful风格数据,Restful请求数据一般会在URL地址上携带,可以使用注解 @PathVariable(占位符参数名称)
xml
http://localhost:8080/spring_webmvc/param2/10
java
@GetMapping("/param2/{id}")
public String param2(@PathVariable("id") Integer id){
System.out.println("id: "+id);
return "/index.jsp";
}

接收文件上传的数据,文件上传的表单需要一定的要求,如下:
- 表单的提交方式必须是POST
- 表单的enctype属性必须是multipart/form-data
- 文件上传项需要有name属性
xml
<form action="" enctype="multipart/form-data">
<input type="file" name="myFile">
</form>
服务器端,由于映射器适配器需要文件上传解析器,而该解析器默认未被注册,所以手动注册
xml
<!--配置文件上传解析器,注意:id的名字是固定写法-->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="UTF-8"/><!--文件的编码格式 默认是ISO8859-1-->
<property name="maxUploadSizePerFile" value="1048576"/><!--上传的每个文件限制的大小 单位字节-->
<property name="maxUploadSize" value="3145728"/><!--上传文件的总大小-->
<property name="maxInMemorySize" value="1048576"/><!--上传文件的缓存大小-->
</bean>
而CommonsMultipartResolver底层使用的Apache的是Common-fileuplad等工具API进行的文件
xml
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.7</version>
</dependency>
使用MultipartFile类型接收上传文件
xml
http://localhost:8080/spring_webmvc/param3
java
@PostMapping("/param3")
public String param3(@RequestBody MultipartFile myFile){
System.out.println("myFile: "+myFile);
return "/index.jsp";
}

java
@PostMapping("/fileUpload")
public String fileUpload(@RequestBody MultipartFile myFile) throws IOException {
System.out.println(myFile);
//获得上传的文件的流对象
InputStream inputStream = myFile.getInputStream();
//使用commons-io存储到D:\soft\abc.txt位置
FileOutputStream outputStream = new
FileOutputStream("D:\\soft\\"+myFile.getOriginalFilename());
IOUtils.copy(inputStream,outputStream);
//关闭资源
inputStream.close();
outputStream.close();
return "/index.jsp";
}
接收Http请求头数据,接收指定名称的请求头
xml
http://localhost:8080/spring_webmvc/param4
java
@GetMapping("/param4")
public String param4(@RequestHeader("Accept-Encoding") String headValue){
System.out.println("Accept-Encoding: "+headValue);
return "/index.jsp";
}

接收所有的请求
xml
http://localhost:8080/spring_webmvc/headersMap
java
@GetMapping("/headersMap")
public String headersMap(@RequestHeader Map<String,String> map){
map.forEach((k,v)->{
System.out.println(k+":"+v);
});
return "/index.jsp";
}

获得客户端携带的Cookie数据
java
@GetMapping("/cookies")
public String cookies(@CookieValue(value = "JSESSIONID",defaultValue = "") String jsessionid){
System.out.println(jsessionid);
return "/index.jsp";
}

获得转发Request域中数据,在进行资源之间转发时,有时需要将一些参数存储到request域中携带给下一个资源
java
@GetMapping("/request1")
public String request1(HttpServletRequest request){
//存储数据
request.setAttribute("username","heima");
return "forward:/request2";
}
@GetMapping("/request2")
public String request2(@RequestAttribute("username") String username){
System.out.println(username);
return "/index.jsp";
}
JavaWeb常用对象获取
获得Javaweb常见原生对象,有时在我们的Controller方法中需要用到Javaweb的原生对象,例如:Request、Response等,我们只需要将需要的对象以形参的形式写在方法上,SpringMVC框架在调用Controller方法时,会自动传递实参:
xml
http://localhost:8080/spring_webmvc/param5
java
@GetMapping("/param5")
public String param5(HttpServletRequest request, HttpServletResponse response){
System.out.println(request);
System.out.println(response);
return "/index.jsp";
}

请求静态资源
静态资源请求失效的原因,当DispatcherServlet的映射路径配置为 / 的时候,那么就覆盖的Tomcat容器默认的缺少Servlet,在Tomcat的config目录下有一个web.xml 是对所有的web项目的全局配置,其中有如下配置:
xml
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
url-pattern配置为 / 的Servlet我们称其为缺省的Servlet,作用是当其他Servlet都匹配不成功时,就找缺省的Servle,静态资源由于没有匹配成功的Servlet,所以会找缺省的DefaultServlet,该DefaultServlet具备二次去匹配静态资源的功能。但是我们配置DispatcherServlet后就将其覆盖掉了,而DispatcherServlet会将请求的静态资源的名称当成Controller的映射路径去匹配,即静态资源访问不成功
静态资源请求的三种解决方案:
第一种方案,可以再次激活Tomcat的DefaultServlet,Servlet的url-pattern的匹配优先级是:精确匹配>目录匹配>扩展名匹配>缺省匹配,所以可以指定某个目录下或某个扩展名的资源使用DefaultServlet进行解析:
xml
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/img/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
第二种方式,在spring-mvc.xml中去配置静态资源映射,匹配映射路径的请求到指定的位置去匹配资源
xml
<!-- mapping是映射资源路径,location是对应资源所在的位置 -->
<mvc:resources mapping="/img/*" location="/img/"/>
<mvc:resources mapping="/css/*" location="/css/"/>
<mvc:resources mapping="/css/*" location="/js/"/>
<mvc:resources mapping="/html/*" location="/html/"/>
第三种方式,在spring-mvc.xml中去配置< mvc:default-servlet-handler >,该方式是注册了一个DefaultServletHttpRequestHandler 处理器,静态资源的访问都由该处理器去处理,这也是开发中使用最多的
xml
<mvc:default-servlet-handler/>
注解驱动 < mvc:annotation-driven> 标签
静态资源配置的第二第三种方式我们可以正常访问静态资源了,但是Controller又无法访问了,报错404,即找不到对应的资源
第二种方式是通过SpringMVC去解析mvc命名空间下的resources标签完成的静态资源解析,第三种方式式通过SpringMVC去解析mvc命名空间下的default-servlet-handler标签完成的静态资源解析,根据前面所学习的自定义名空间的解析的知识,可以发现不管是以上哪种方式,最终都会注册SimpleUrlHandlerMapping
java
public BeanDefinition parse(Element element, ParserContext context) {
//创建SimpleUrlHandlerMapping类型的BeanDefinition
RootBeanDefinition handlerMappingDef =
new RootBeanDefinition(SimpleUrlHandlerMapping.class);
//注册SimpleUrlHandlerMapping的BeanDefinition
context.getRegistry().registerBeanDefinition(beanName, handlerMappingDef);
}
又结合组件浅析知识点,一旦SpringMVC容器中存在 HandlerMapping 类型的组件时,前端控制器DispatcherServlet在进行初始化时,就会从容器中获得HandlerMapping ,不在加载 dispatcherServlet.properties中默认处理器映射器策略,那也就意味着RequestMappingHandlerMapping不会被加载到了。
手动将RequestMappingHandlerMapping也注册到SpringMVC容器中就可以了,这样DispatcherServlet在进行初始化时,就会从容器中同时获得RequestMappingHandlerMapping存储到DispatcherServlet中名为
handlerMappings的List集合中,对@RequestMapping 注解进行解析
xml
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
根据上面的讲解,可以总结一下,要想使用@RequestMapping正常映射到资源方法,同时静态资源还能正常访问,还可以将请求json格式字符串和JavaBean之间自由转换,我们就需要在spring-mvc.xml中尽心如下配置:
xml
<!-- 显示配置RequestMappingHandlerMapping -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
<!-- 显示配置RequestMappingHandlerAdapter -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
</list>
</property>
</bean>
<!--配置DefaultServletHttpRequestHandler-->
<mvc:default-servlet-handler/>
这么复杂繁琐的配置,是不是看上去有点头大?Spring是个"暖男",将上述配置浓缩成了一个简单的配置标签,那就是mvc的注解驱动,该标签内部会帮我们注册RequestMappingHandlerMapping、注册RequestMappingHandlerAdapter并注入Json消息转换器等,上述配置就可以简化成如下:
xml
<!--mvc注解驱动-->
<mvc:annotation-driven/>
<!--配置DefaultServletHttpRequestHandler-->
<mvc:default-servlet-handler/>
PS:< mvc:annotation-driven> 标签在不同的版本中,帮我们注册的组件不同,Spring 3.0.X 版本注册是DefaultAnnotationHandlerMapping 和 AnnotationMethodHandlerAdapter,由于框架的发展,从Spring 3.1.X
开始注册组件变为 RequestMappingHandlerMapping和RequestMappingHandlerAdapt
总结
通过本篇博客,我们不仅掌握了Spring MVC的请求处理流程,还了解了其背后的设计理念。无论是简单的表单提交还是复杂的RESTful服务,Spring MVC都能提供良好的支持。希望读者在今后的开发中,能够将所学知识运用于实践中,提升自己的开发技能。