SpringMVC的请求处理

一、请求映射路径的配置

配置映射路径,映射器处理器才能找到Controller的方法资源,目前主流映射路径配置方式就是@RequestMapping

@RequestMapping注解,主要使用在控制器的方法上,用于标识客户端访问资源路径,常用的属性有value、path、method、headers、params等。当@RequestMapping只有一个访问路径需要指定时,使用value属性、path属性或省略value和path,当有多个属性时,value和path不能省略

java 复制代码
    @RequestMapping(value = "/show")//使用value属性指定一个访问路径
    public String show(){}
    @RequestMapping(value = {"/show","/haohao","/abc"})//使用value属性指定多个访问路径
    public String show(){}

    @RequestMapping(path = "/show")//使用path属性指定一个访问路径
    public String show(){}
    @RequestMapping(path = {"/show","/haohao","/abc"})//使用path属性指定多个访问路径
    public String show(){}

    @RequestMapping("/show")//如果只设置访问路径时,value和path可以省略
    public String show(){}
    @RequestMapping({"/show","/haohao","/abc"})
    public String show(){}

当@RequestMapping 需要限定访问方式时,可以通过method属性设置

java 复制代码
    //请求地址是/show,且请求方式必须是POST才能匹配成功
    @RequestMapping(value = "/show",method = RequestMethod.POST)
    public String show(){}

method的属性值是一个枚举类型,源码如下:

java 复制代码
    public enum RequestMethod {
    	GET,
    	HEAD,
    	POST,
    	PUT,
    	PATCH,
    	DELETE,
    	OPTIONS,
    	TRACE;
    	private RequestMethod() {
    	}
    }

@GetMapping,当请求方式是GET时,我们可以使用@GetMapping替代@RequestMapping

java 复制代码
    @GetMapping("/show")
    public String show(){}

@PostMapping,当请求方式是POST时,我们可以使用@PostMapping替代@RequestMapping

java 复制代码
    @PostMapping("/show")
    public String show(){}

@RequestMapping 在类上使用,@RequestMapping 、@GetMapping、@PostMapping还可以使用在Controller类上,使用在类上后,该类所有方法都共用该@RequestMapping设置的属性,访问路径则为类上的映射地址+方法上的映射地址,例如:

java 复制代码
    @Controller
    @RequestMapping("/xxx")
    public class UserController implements ApplicationContextAware, ServletContextAware {
    	@GetMapping("/aaa")
    	public ModelAndView aaa(HttpServletResponse response) throws IOException, ModelAndViewDefiningException {
    		return null;
    	}
    }

此时的访问路径为:/xxx/aaa

二、请求数据的接收

1. 接收普通请求数据

接收普通请求数据,当客户端提交的数据是普通键值对形式时,直接使用同名形参接收即可 username=haohao&age=35

java 复制代码
    @GetMapping("/show")
    public String show(String username, int age){
    	System.out.println(username+"=="+age);
    	return "/index.jsp";
    }

接收普通请求数据,当请求参数的名称与方法参数名不一致时,可以使用@RequestParam注解进行标注 username=haohao&age=35

java 复制代码
    @GetMapping("/show")
    public String show(@RequestParam(name = "username",required = true) String name, int age){
    	System.out.println(name+"=="+age);
    	return "/index.jsp";
    }

2. 接收数组或集合数据

接收数组或集合数据,客户端传递多个同名参数时,可以使用数组接收 hobbies=eat&hobbies=sleep

java 复制代码
    @GetMapping("/show")
    public String show(String[] hobbies){
    	for (String hobby : hobbies) {
    		System.out.println(hobby);
    	}
    	return "/index.jsp";
    }

客户端传递多个同名参数时,也可以使用单列集合接收,但是需要使用@RequestParam告知框架传递的参数是要同名设置的,不是对象属性设置的

java 复制代码
    @GetMapping("/show")
    public String show(@RequestParam List<String> hobbies){
    	for (String hobby : hobbies) {
    		System.out.println(hobby);
    	}
    	return "/index.jsp";
    }

接收数组或集合数据,客户端传递多个不同命参数时,也可以使用Map<String,Object> 进行接收,同样需要用@RequestParam 进行修饰 username=haohao&age=18

java 复制代码
    @PostMapping("/show")
    public String show(@RequestParam Map<String,Object> params) {
    	params.forEach((key,value)->{
    		System.out.println(key+"=="+value);
    	});
    	return "/index.jsp";
    }

3. 接收实体JavaBean属性数据

接收实体JavaBean属性数据,单个JavaBean数据:提交的参数名称只要与Java的属性名一致,就可以进行自动封装 username=haohao&age=35&hobbies=eat&hobbies=sleep

java 复制代码
    public class User {
    	private String username;
    	private Integer age;
    	private String[] hobbies;
    	private Date birthday;
    	private Address address;
    	//... 省略get和set方法 ... 
    }
java 复制代码
    @GetMapping("/show")
    public String show(User user) {
    	System.out.println(user);
    	return "/index.jsp";
    }

接收实体JavaBean属性数据,嵌套JavaBean数据:提交的参数名称用 . 去描述嵌套对象的属性关系即可 username=haohao&address.city=tianjin&address.area=jinghai

java 复制代码
    public class Address {
        private String city;
        private String area;
        //... 省略get和set方法 ... 
    }
java 复制代码
    // http://localhost/param6?username=haohao&address.city=tianjin&address.area=jinghai
    @GetMapping("/param6")
    public String param6(User user){
        System.out.println(user);
        return "/index.jsp";
    }

4. 接收Json数据格式数据

接收Json数据格式数据,Json数据都是以请求体的方式提交的,且不是原始的键值对格式的,所以我们要使用@RequestBody注解整体接收该数据。

json 复制代码
    {
    	"username":"haohao",
    	"age":18,
    	"hobbies":["eat","sleep"],
    	"birthday":"1986-01-01",
    	"address":{
    		"city":"tj",
    		"area":"binhai"
    	}
    }
java 复制代码
    @PostMapping("/show6")
    public String show6(@RequestBody String body){
    	System.out.println(body);
    	return "/index.jsp";
    }

使用Json工具( jackson )将Json格式的字符串转化为JavaBean进行操作

xml 复制代码
    <dependency>
    	<groupId>com.fasterxml.jackson.core</groupId>
    	<artifactId>jackson-databind</artifactId>
    	<version>2.9.0</version>
    </dependency
java 复制代码
    @PostMapping("/show")
    public String show(@RequestBody String body) throws IOException {
    	System.out.println(body);
    	// 获得ObjectMapper
    	ObjectMapper objectMapper = new ObjectMapper();
    	// 将json格式字符串转化成指定的User
    	User user = objectMapper.readValue(body, User.class);
    	System.out.println(user);
    	return "/index.jsp";
    }

配置RequestMappingHandlerAdapter,指定消息转换器,就不用手动转换json格式字符串了

xml 复制代码
    <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>
java 复制代码
    @PostMapping("/show")
    public String show(@RequestBody User user){
    	System.out.println(user);
    	return "/index.jsp";
    }

接收Json数据格式数据,使用Map接收json格式字符串

java 复制代码
    @PostMapping("/show")
    public String show(@RequestBody Map map){
    	System.out.println(map);
    	return "/index.jsp";
    }

5. 接收Restful风格数据

什么是Rest风格? Rest(Representational State Transfer)表象化状态转变(表述性状态转变),在2000年被提出,基于HTTP、URI、xml、JSON等标准和协议,支持轻量级、跨平台、跨语言的架构设计。是Web服务的一种新网络应用程序的设计风格和开发方式。
Restful风格的请求,常见的规则有如下三点:

① 用URI表示某个模块资源,资源名称为名词

② 用请求方式表示模块具体业务动作

例如:GET表示查询、POST表示插入、PUT表示更新、DELETE表示删除

③ 用HTTP响应状态码表示结果

国内常用的响应包括三部分:状态码、状态信息、响应数据

json 复制代码
    {
    	"code":200,
    	"message":"成功",
    	"data":{
    		"username":"haohao",
    		"age":18
    	}
    }

    {
    	"code":300,
    	"message":"执行错误",
    	"data":"",
    }

接收Restful风格数据,Restful请求数据一般会在URL地址上携带,可以使用注解 @PathVariable(占位符参数名称) http://localhost/user/100

less 复制代码
    @PostMapping("/user/{id}")
    public String findUserById(@PathVariable("id") Integer id){
    	System.out.println(id);
    	return "/index.jsp";
    }

请求URL资源地址包含多个参数情况 http://localhost/user/haohao/18

java 复制代码
    @PostMapping("/user/{username}/{age}")
    public String findUserByUsernameAndAge(@PathVariable("username") String username, @PathVariable("age") Integer age){
    	System.out.println(username+"=="+age);
    	return "/index.jsp";
    }

接收文件上传的数据,文件上传的表单需要一定的要求,如下: ⚫ 表单的提交方式必须是POST ⚫ 表单的enctype属性必须是multipart/form-data ⚫ 文件上传项需要有name属性

html 复制代码
    <form action="" enctype="multipart/form-data" method="post" >
    	<input type="file" name="myFile">
    </form>

服务器端,由于映射器适配器需要文件上传解析器,而该解析器默认未被注册,所以需要手动注册

xml 复制代码
    <!--配置文件上传解析器,注意:id的名字是固定写法,id必须是multipartResolver,因为Spring容器是根据id="multipartResolver"获取的CommonsMultipartResolver-->
    <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>

使用MultipartFile类型接收上传文件

java 复制代码
    @PostMapping("/fileUpload")
    public String fileUpload(@RequestBody MultipartFile myFile) throws IOException {
    	System.out.println(myFile);
    	// 获得上传的文件的流对象
    	InputStream inputStream = myFile.getInputStream();
    	// 使用commons-io存储到C:\haohao\abc.txt位置
    	FileOutputStream outputStream = new 
    	FileOutputStream("C:\\Users\\haohao\\" + myFile.getOriginalFilename());
    	IOUtils.copy(inputStream,outputStream);
    	// 关闭资源
    	inputStream.close();
    	outputStream.close();
    	return "/index.jsp";
    }

接收Http请求头数据,接收指定名称的请求头

java 复制代码
    @GetMapping("/headers")
    public String headers(@RequestHeader("Accept-Encoding") String acceptEncoding){
    	System.out.println("Accept-Encoding:" + acceptEncoding);
    	return "/index.jsp";
    }

接收所有的请求头信息

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","haohao");
    	return "/request2";
    }

    @GetMapping("/request2")
    public String request2(@RequestAttribute("username") String username){
    	System.out.println(username);
    	return "/index.jsp";
    }

请求参数乱码的解决方案,Spring已经提供好的CharacterEncodingFilter来进行编码过滤

xml 复制代码
    <!--配置全局的编码过滤器-->
    <filter>
    	<filter-name>CharacterEncodingFilter</filter-name>
    	<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    	<init-param>
    		<param-name>encoding</param-name>
    		<param-value>UTF-8</param-value>
    	</init-param>
    </filter>
    <filter-mapping>
    	<filter-name>CharacterEncodingFilter</filter-name>
    	<url-pattern>/*</url-pattern>
    </filter-mapping>

三、Javaweb常用对象获取

获得Javaweb常见原生对象,有时在我们的Controller方法中需要用到Javaweb的原生对象,例如:Request、Response等,我们只需要将需要的对象以形参的形式写在方法上,SpringMVC框架在调用Controller方法时,会自动传递实参:

java 复制代码
    @GetMapping("/javawebObject")
    public String javawebObject(HttpServletRequest request, HttpServletResponse response, HttpSession session){
    	System.out.println(request);
    	System.out.println(response);
    	System.out.println(session);
    	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都匹配不成功时,就找缺省的Servlet,静态资源由于没有匹配成功的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不会被加载到了,所以不会对@RequestMapping 注解进行解析。
因此,需要手动将RequestMappingHandlerMapping也注册到SpringMVC容器中就可以了,这样DispatcherServlet在进行初始化时,就会从容器中同时获得RequestMappingHandlerMapping存储到DispatcherServlet中名为handlerMappings的List集合中,对@RequestMapping 注解进行解析。

ini 复制代码
    <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和RequestMappingHandlerAdapter

相关推荐
2501_903238651 小时前
自定义登录页面的Spring Security实践
java·后端·spring·个人开发
飞翔的佩奇2 小时前
Java项目: 基于SpringBoot+mybatis+maven+mysql实现的图书管理系统(含源码+数据库+答辩PPT+毕业论文)
java·数据库·spring boot·mysql·spring·毕业设计·图书管理
Future_yzx4 小时前
Spring MVC学习——发送请求(@RequestMapping注解及请求参数绑定)
学习·spring·mvc
程序猿零零漆9 小时前
SpringCloud系列教程:微服务的未来(二十)Seata快速入门、部署TC服务、微服务集成Seata
java·spring·spring cloud·微服务
Miketutu19 小时前
Spring MVC消息转换器
java·spring
小小虫码21 小时前
项目中用的网关Gateway及SpringCloud
spring·spring cloud·gateway
带刺的坐椅1 天前
无耳科技 Solon v3.0.7 发布(2025农历新年版)
java·spring·mvc·solon·aop
精通HelloWorld!1 天前
使用HttpClient和HttpRequest发送HTTP请求
java·spring boot·网络协议·spring·http
LUCIAZZZ1 天前
基于Docker以KRaft模式快速部署Kafka
java·运维·spring·docker·容器·kafka
拾忆,想起1 天前
如何选择Spring AOP的动态代理?JDK与CGLIB的适用场景
spring boot·后端·spring·spring cloud·微服务