学习资源:动力节点的2020最新SpringMVC教程【IDEA版】-springmvc从入门到精通
1、@RequestMapping 请求映射
在控制器的方法上使用,表示要对 value 属性所指定的 URI 请求进行处理与响应。
1.1、指定模块名称
通过 @RequestMapping 注解可以定义控制器对于请求的映射规则。 该注解可以注解在方法上,也可以注解在类上,但意义是不同的。 value 属性值常以 "/" 开始。
@RequestMapping 的 value 属性用于定义所匹配请求的 URI。但对于注解在方法上与类上,其 value 属性所指定的 URI,意义是不同的。
一个 @Controller 所注解的类中,可以定义多个控制器方法。当然,不同的控制器方法所匹配的 URI 是不同的。 这些不同的 URI 被指定在注解于方法之上的 @RequestMapping 的 value 属性中。 但若这些请求具有相同的 URI 部分,则这些相同的 URI, 可以被抽取到注解在类之上的 @RequestMapping 的 value 属性中,此时的这个 URI 表示模块的名称。 URI 的请求是相对于 Web 的根目录。
换个角度说,要访问控制器的指定方法,必须要在方法指定 URI 之前加上控制器类前定义的模块名称。
@Controller
public class FirstController {
@RequestMapping("/test/some.do")
public ModelAndView doSome(){
}
@RequestMapping("/test/other.do")
public ModelAndView doOther(){
}
}
//可将相同的 uri 提取到类上
@Controller
@RequestMapping("/test")
public class FirstController {
@RequestMapping("some.do")
public ModelAndView doSome(){
}
@RequestMapping("other.do")
public ModelAndView doOther(){
}
}
1.2、定义请求提交方式
对于 @RequestMapping,其有一个属性 method,用于对被注解方法所处理请求的提交方式进行限制,即只有满足该 method 属性指定的提交方式的请求,才会执行该被注解方法。
Method 属性的取值为 RequestMethod 枚举常量。常用的为 RequestMethod.GET 与 RequestMethod.POST,分别表示提交方式的匹配规则为 GET 与 POST 提交。
客户端浏览器常用的请求方式,及其提交方式:
不指定 method 属性,则无论是 GET 还是 POST 提交方式,均可匹配,即对于请求的提交方式无要求.
@RequestMapping("/other.do")
public ModelAndView doOther(){
}
指定了控制器方法匹配的请求提交方式为 POST,则相当于指定了请求发送的方式: 要么使用表单请求,要么使用 AJAX 请求,其它请求方式都会被被禁用。
@RequestMapping(value = {"/other.do"}, method = RequestMethod.POST)
public ModelAndView doOther(){
}
指定请求方式为 GET ,则超链接、GET 型表单都可发送请求到控制器。
@RequestMapping(value = {"/other.do"}, method = RequestMethod.GET)
public ModelAndView doOther(){
}
2、控制器方法的参数
控制器方法可以包含以下四类参数,这些参数会在系统调用时由系统自动赋值,即程序员可在方法内直接使用:
- HttpServletRequest
- HttpServletResponse
- HttpSession
- Model
- ModelMap
- 请求中所携带的请求参数
@RequestMapping(value = "请求地址")
public 返回值 自定义方法名(HttpRequest request,
HttpServletResponse response,
HttpSession session,
Model model,
请求中携带参数...){
}
2.1、逐个参数接收(控制器方法形参接收)
若请求中的参数个数较少,则只要保证 uri 中的请求参数名与该请求处理的控制器方法的参数名相同即可,框架会把同名的请求参数赋值给同名的形参,对形参的顺序没有要求。
SpringMVC 使用 request 对象接收请求参数,String name= request.getParameter("name");
、String age= request.getParameter("age");
,框架会提供类型转换的功能,能把String转为 int ,long , float, double等类型。
该方式是用于请求参数数量少的情况。
GET方式方便查看 uri
<form action="test/register.do" method="GET">
姓名:<input type="text" name="name"/><br/>
年龄:<input type="text" name="age" /><br/>
<input type="submit" value="注册"/>
</form>
该表单的请求 uri:http://localhost:8082/springmvc/test/register.do?name=Linda&age=15
@Controller
@RequestMapping("/test")
public class FirstController {
@RequestMapping("/register.do", method= RequestMethod.GET)
public ModelAndView doRegister(Integer age, String name){
ModelAndView mv = new ModelAndView();
mv.addObject("registerName", );
mv.addObject("registerAge", );
mv.setViewName("show");
return mv;
}
}
2.1.2、字符集过滤器解决请求中参数乱码问题
请求使用 POST 方式,会出现请求中文参数乱码问题。
<form action="test/register.do" method="POST">
姓名:<input type="text" name="name"/><br/>
年龄:<input type="text" name="age" /><br/>
<input type="submit" value="注册"/>
</form>
Spring 对于请求参数中的中文乱码问题,给出了专门的字符集过滤器: spring-web-5.2.5.RELEASE.jar 的
org.springframework.web.filter 包下的 CharacterEncodingFilter 类。
<!-- 注册字符集过滤器:解决post请求乱码的问题 -->
<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>
<!-- 强制 request 使用 encoding -->
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<!-- 强制 response 使用 encoding -->
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<!-- 指定哪些 url 使用该过滤器,/* 表示所有请求 -->
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
2.1.2、@RequestParam 校正请求参数名
校正请求参数名,是指若请求 URL 所携带的参数名称与处理方法中指定的参数名不相同时, 则需在处理方法参数前, 添加一个注解@RequestParam("请求参数名"),指定请求 URL 所携带参数的名称。该注解是对控制器方法参数进行修饰的。
value 属性,String 类型,指定请求参数的名称。
required boolean 类型,默认是 true,true 表示请求中必须带有参数,false 则反之。如,设置为 true,发送请求:http://localhost:8082/springmvc/test/register.do?r_name=Linda&r_age=15 会出现 400 的错误。
<form action="test/register.do" method="POST">
姓名:<input type="text" name="r_name"/><br/>
年龄:<input type="text" name="r_age" /><br/>
<input type="submit" value="注册"/>
</form>
@Controller
@RequestMapping("/test")
public class FirstController {
@RequestMapping("/register.do", method= RequestMethod.POST)
public ModelAndView doRegister(@RequestParam(value="r_age", required=false)Integer age,
@RequestParam(value="r_name", required=false)String name){
}
}
2.2、对象接收参数
若请求中有多个参数,逐个接收参数较为繁琐,可以将控制器方法的参数定义为一个对象,只要保证请求参数名与这个对象的属性同名即可。
当控制器方法的形参是 Java 对象,SpringMVC 框架会使用参数类的无参构造器创建一个参数对象,并调用 setter 将请求参数一一赋值给参数对象的同名属性。
定义接收请求参数的类:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Registrant {
private String name;
private Integer age;
}
<form action="test/register.do" method="POST">
姓名:<input type="text" name="r_name"/><br/>
年龄:<input type="text" name="r_age" /><br/>
<input type="submit" value="注册"/>
</form>
@Controller
@RequestMapping("/test")
public class FirstController {
@RequestMapping("/register.do", method= RequestMethod.GET)
public ModelAndView doRegister(Registrant registrant){
ModelAndView mv = new ModelAndView();
mv.addObject("name",registrant.getName());
mv.addObject("age",registrant.getAge());
mv.addObject("registrant",registrant);
mv.setViewName("show");
return mv;
}
}
3、控制器方法的返回值
3.1、返回 ModelAndView
表示返回 数据+视图。
若控制器方法处理完后,需要跳转到其它资源,且又要在跳转的资源间传递数据, 此时控制器方法返回 ModelAndView 比较好。当然,若要返回 ModelAndView,则控制器方法中需要定义 ModelAndView 对象。
Model 中的数据被存储在 request 作用域中,SringMVC 默认采用 转发 的方式跳转到视图,本次请求结束后,模型中的数据被销毁。
modelAndView.addObject("registerName", name);
modelAndView.addObject("registerAge", age);
modelAndView.setViewName("show");
在使用时, 若该控制器方法只是进行跳转而不传递数据,或只是传递数据而并不向任何资源跳转(如对页面的 Ajax 异步响应),此时若返回 ModelAndView,则将总是有一部分多余: 要么 Model 多余,要么 View 多余。 即此时返回 ModelAndView 将不合适。
3.2、返回 String(视图)
表示返回视图。
控制器方法返回的字符串可以指定逻辑视图名,通过视图解析器解析可以将其转换为物理视图地址;也可以去除视图解析器,使用视图的完整路径名。
@RequestMapping(value = "/test/register.do", method = RequestMethod.POST)
public String doRegister(HttpRequest request, Integer age, String name){
request.setAttribute("name", name);
request.setAttribute("age", age);
//有视图解析器的情况
return "show";
}
3.3、返回 void
对于控制器方法返回 void 的主要应用场景:AJAX 响应(异步请求,页面局部更新)。
若控制器对请求处理后,无需跳转到其它任何资源,此时可以让控制器方法返回 void。
导入 GSON 依赖:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.10.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.3</version>
</dependency>
引入 jquery 库,页面发起 ajax 请求:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<script type="text/javascript" src="js/jquery-3.4.1.js"></script>
<script type="text/javascript">
$(function(){
$("button").click(function(){
$.ajax({
url:"returnVoid-ajax.do",
data:{
name:"张三",
age:20
},
type:"post",
// dataType 加不加都可以
dataType:"json",
success:function(resp){
//resp从服务器端返回的是json格式的字符串 {"name":"zhangsan","age":20}
//jquery会把字符串转为json对象, 赋值给resp形参。
alert(resp.name + "" + resp.age);
}
})
})
})
</script>
</head>
<body>
<button id="btn">发起ajax请求</button>
</body>
</html>
控制器方法:
@RequestMapping(value = "/returnVoid-ajax.do")
public void doAjax(HttpServletResponse response, Integer age, String name) throws IOException {
// 处理ajax,使用json做数据的格式,使用 registrant 表示处理结果
Registrant registrant = new Registrant("张三", 19);
// 把结果的对象转为json格式的数据
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(registrant);
// 输出数据,响应ajax的请求
PrintWriter writer = response.getWriter();
writer.print(json);
writer.flush();
writer.close();
}
3.4、返回 Object
控制器方法也可以返回 Object 对象。 这个 Object 可以是 Integer, String, 自定义对象,Map, List 等。但返回的对象不是作为逻辑视图出现的,而是作为直接在原页面显示的数据出现。
主要使用场景:响应 ajax 请求,将响应数据以 json 格式输出到页面。
返回对象,需要使用 @ResponseBody 注解, 将转换后的 JSON 数据放入到响应体中。
实现步骤:
- 加入处理 json 的依赖。
由于返回 Object 数据,一般都是将数据转化为了 JSON 对象后传递给浏览器页面的。而这个由 Object 转换为 JSON,是由 Jackson 工具完成的。所以需要导入 Jackson 的相关 Jar 包(springmvc 默认使用 jackson)。
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.10.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.3</version>
</dependency>
- 在 springmvc 配置文件中配声明注解驱动。
将 Object 数据转化为 JSON 数据,需要由消息转换器 HttpMessageConverter 完成。而转换器的开启,需要由 <mvc:annotation-driven/> 来完成。
SpringMVC 使用消息转换器 HttpMessageConverter 实现请求数据和对象,控制器方法返回对象和响应输出之间的自动转换。
<!-- 注解驱动 -->
<!-- 开启消息转换器 -->
<mvc:annotation-driven/>
- 在控制器方法上使用 @ResponseBody 注解,作用是完成下面代码的功能
PrintWriter pw = reponse.getWrite();
pw.print(json);
pw.flush;
pw.close;
3.4.1、返回自定义类型对象
@RequestMapping(value = "/returnRegistrant.do")
@ResponseBody
public List<Registrant> doReturn() {
Registrant registrant = new Registrant("张三", 31);
return registrant;
}
3.4.2、返回 List
@RequestMapping(value = "/returnList.do")
@ResponseBody
public List<Registrant> doReturnList() {
Registrant registrant1 = new Registrant("张三", 31);
Registrant registrant2 = new Registrant("张三", 31);
List<Registrant> list = new ArrayList<>();
list.add(registrant1);
list.add(registrant2);
return list;
}
3.4.3、返回 String
若要返回非中文字符串,将前面返回数值型数据的返回值直接修改为字符串即可。但若返回的字符串中带有中文字符,则接收方页 面将会出现乱码(中文字符转换为 json 默认使用的是 IOS-8859-1),此时需要使用 @RequestMapping 的 produces 属性指定字符集。
produces,产品,结果,即该属性用于设置输出结果类型。
@RequestMapping(value = "/returnString.do", produces="text/plain;charset=utf-8")
@ResponseBody
public String doreturnString() {
return "SpringMVC 注解开发真快!";
}
4、url-pattern
<url-pattern/> 指定了请求地址与 servlet 的映射关系,如 *.do 的请求要交与 "springmvc " 这个框架的 servlet 处理。
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
然而 工程名/index.jsp、工程名/静态资源(.js、.html、.jpg等)在浏览器中可以直接访问到,是谁处理的它们呢?—— Tomcat。为什么 Tomcat 能处理这些请求呢,其原理也是使用 servlet ,其内部有一个名为 default 的 servlet,用来处理静态资源和未映射到其他 servlet 的请求。 "/" 表示 default 要处理的是静态资源。
<!-- The default servlet for all web applications, that serves static -->
<!-- resources. It processes all requests that are not mapped to other -->
<!-- servlets with servlet mappings (defined either here or in your own -->
<!-- web.xml file). -->
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
4.1、*.后缀
在没有特殊要求的情况下, SpringMVC 的中央调度器 DispatcherServlet 的 <url-pattern/> 常使用后辍匹配方式,如写为 *.do 或者 *.action 、 *.mvc 等。
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.do</url-pattern>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
4.2、/
/ 表示映射所有的请求到 DispatcherServlet,包括静态资源和动态资源的请求。
当给 DispatcherServlet 的映射配置为 "/" 后,DispatcherServlet 会替代 default ,但是默认情况下 DispatcherServlet 没有处理静态资源的能力,因为控制器不能访问静态资源,这样就会造成所有静态资源无法访问,404。
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
4.3、静态资源访问
<url-pattern/> 的值并不是说写为 / 后,静态资源就无法访问了,经过一些配置后,该问题也是可以解决的。
4.3.1、声明 <mvc:default-servlet-handler/>
在 springmvc 配置文件中声明 <mvc:default-servlet-handler/>
声明了 <mvc:default-servlet-handler /> 后,springmvc 框架会在容器中创建 DefaultServletHttpRequestHandler 处理器对象,它会像一个检查员,对进入 DispatcherServlet 的 URL 进行筛查,如果发现是访问静态资源的请求,就将该请求转由 Web 应用服务器默认的 Servlet 处理(一般的服务器都有默认的 Servlet)。
<mvc:default-servlet-handler/>
4.3.2、声明 <mvc:resources/>
在 Spring3.0 版本后, Spring 定义了专门用于处理静态资源访问请求的处理器 ResourceHttpRequestHandler。并且添加了<mvc:resources/>标签,专门用于解决静态资源无法访问问题。 需要在 springmvc 配置文件中添加如下形式的配置:
<!-- 为webapp下的每个类别的静态资源一一进行映射 -->
<!-- url 中的 ** 表示该路径的所有请求 -->
<mvc:resources mapping=" /images/**" location="/images/"/>
<mvc:resources mapping="/html/**" location="/html/" />
<mvc:resources mapping="/js/**" location="/js/"/>
......
还是太麻烦!将静态资源统一放置在 webapp/static/ 下:
<mvc:resources mapping="/static/**" location="/static/"/>
4.3.3、为1、2方式声明注解驱动
不使用服务器自带的 servlet 处理静态资源,使用上面两种方式处理的话,会发生动态资源和静态资源冲突的问题,为此需要在 springmvc 配置文件加入:
<mvc:annotation-driven/>