Spring MVC 入门
MVC 简介:
MVC是模型(Model)、视图(View)、控制器(Controller)的简写,是一种软件设计规范:
Model(模型):数据模型,提供要展示的数据,因此包含数据和行为,可以认为是领域模型或JavaBean组件(包含数据和行为),不过现在一般都分离开来:Value Object(数据Dao) 和 服务层(行为Service)。也就是模型提供了模型数据查询和模型数据的状态更新等功能,包括数据和业务。
View(视图):负责进行模型的展示,一般就是我们见到的用户界面,客户想看到的东西。
Controller(控制器):接收用户请求,委托给模型进行处理(状态改变),处理完毕后把返回的模型数据返回给视图,由视图负责展示。也就是说控制器做了个调度员的工作。
Spring MVC 简单实例:
1. 导入所需依赖:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> <scope>test</scope> </dependency>
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.8.RELEASE</version> </dependency>
<dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency>
<dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>javax.servlet.jsp-api</artifactId> <version>2.3.3</version> <scope>provided</scope> </dependency>
|
servlet 的 maven 依赖分为 javax.servlet-api
和 servlet-api
带 javax 前缀表示最新版本。
2. 配置web.xml :
SpringMVC 思想是用一个前端控制器能拦截所有请求,并智能派发。这个前端控制器是一个 servlet 需要在 web.xml 中进行配置。它提供的前端控制器叫 DispatcherServlet 它继承自 HttpServlet 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param>
<param-name>contextConfigLocation</param-name> <param-value>classpath:spring-mvc.xml</param-value> </init-param>
<load-on-startup>1</load-on-startup> </servlet> <servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
|
3. springmvc 配置文件:
在 resources 文件夹中新建 springmvc-servlet.xml (推荐名,可自定义) 配置文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/> <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> </bean> </beans>
|
这里的 处理器映射器和处理器适配器 是使用了显示的方式去指定,实际1、2也可 **省略 ** springmvc会采用默认的 处理器映射器和处理器适配器 。
4. 编写 Controller 类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class HelloController implements Controller { @Override public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { ModelAndView mv = new ModelAndView(); mv.addObject("msg","HelloSpringMVC!"); mv.setViewName("hello"); return mv; } }
|
ModelAndView 的 setViewName
方法和配置文件中的视图解析器,决定了要跳转的视图和视图的具体位置(通过拼接字符串)。如上表示跳转到 /WEB-INF/jsp/hello.jsp 视图(页面)。hello.jsp中的内容为:
1 2 3 4 5
| <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head><title>hello</title></head> <body>${msg}</body> </html>
|
5. Controller 加入IOC容器:
在 springmvc-servlet.xml 中,注册我们刚刚书写的 HelloController 类。
1 2
| <bean id="/hello" class="io.ruoxijun.HelloController"/>
|
到此启动项目浏览器中输入: localhost:8080/springmvc_demo01/hello 访问,浏览器显示 HelloSpringMVC! 的字样表示成功了。
实例改写注解开发:
1. springmvc-servlet.xml 配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd"> <context:component-scan base-package="io.ruoxijun"/>
<mvc:default-servlet-handler />
<mvc:annotation-driven /> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/> <property name="prefix" value="/WEB-INF/jsp/" /> <property name="suffix" value=".jsp" /> </bean> </beans>
|
2. 注解实现 Controller :
@Controller
注解的类中使用 @RequestMapping
注解的方法就相当于一个 handleRequest 方法。被注解类中的所有方法如果返回值为 String ,并且有具体的页面可以跳转那么就会被视图解析器解析。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @Controller
@RequestMapping("/controller") public class HelloController { @RequestMapping("/hello") public String hello(Model model){ model.addAttribute("msg","hello springmvc"); return "hello"; } }
|
一个类中可以配置多个 RequestMapping 方法,同时它还可以作用在类上相当于为类中所有的请求路径之前加了一层请求的路径映射。
RequestMapping 属性:
常规方式传参:
首先定义一个需要接收两个参数的方法:
1 2 3 4 5
| @RequestMapping("/add") public String add(int a,int b,Model model){ model.addAttribute("msg","两数和为:"+(a+b)); return "hello"; }
|
访问:
localhost:8080/springmvc_demo02/add :直接访问add方法,不传入参数报 500错误。
localhost:8080/springmvc_demo02/add?a=1&b=2 :正确访问方式,使用常规的get方式传参。
1. 模糊匹配(Ant 风格 URL):
1 2 3 4 5 6
| @RequestMapping(value = "/hello?")
@RequestMapping(value = "/*/hello*")
@RequestMapping(value = "/**/hello")
|
2. RestFul 与 PathVariable:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
@RequestMapping("/add/{a}/{b}")
public String add(@PathVariable int a, // 基本数据类型建议使用封装类型接收 @PathVariable("b") int b, // 支持将所有的路径变量 k,v 都封装在一个 Map<String,String> 中 @PathVariable Map<String,String> pv, Model model){ model.addAttribute("msg","两数和为:"+(a-b)); return "hello"; }
|
访问:
localhost:8080/springmvc_demo02/add?a=1&b=2:使用常规的get方式传参,报404错误。
localhost:8080/springmvc_demo02/add/2/1:正确访问方式,RestFul 风格传参。
RestFul 风格 URL:
- 常规 URL 请求设计方式:
1 2 3 4 5
| /getBook?id=1 /deleteBook?id=1 /addBook /updateBook?id=1
|
- Rest 风格 URL 请求设计方式:以简洁的 URL 提交请求,以请求方式来区分对资源的操作。
1 2 3 4 5
| /book/1 /book/1 /book /book/1
|
Sring 对 Rest 请求的支持:
- 浏览器中一般只支持 get、post 请求,springmvc 提供了将普通请求转化为规定请求的 Filter :
org.springframework.web.filter.HiddenHttpMethodFilter
首先在 web.xml 配置此 Filter 。
1 2 3 4 5 6 7 8
| <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>
|
- 实现方式:
创建 post 请求,在请求数据中携带一个名为 _method
的参数, _method
的值就是你想要的请求方式,如:
1 2 3 4 5 6
| <%-- 指定要请求的 rest 风格路径,发送请求方式为 post --%> <form action="/book/1" method="post"> <%-- 指定 _method 参数值,表示 springmvc 要将此 post 请求转化为 DELETE --%> <input name="_method" value="DELETE"> <input type="submit" value="删除图书"> </form>
|
- 高版本 tomcat 的 Rest 请求方法中不支持返回 jsp页面:
tomcat 8 以上返回 Rest 的请求方法返回 jsp 页面时会报 405 的错误,可以在 jsp 页面的头部添加:
<%@page isErrorPage="true" %>
在 jsp page标签中添加 isErrorPage 为 true 让 jsp 将错误封装起来。
3. MatrixVariable 矩阵变量:
@MatrixVariable
:SpringBoot 默认禁用了矩阵变量功能,需要手动开启,矩阵变量必须有url路径变量的支持。
4. method 请求方式:
http 规定的请求方式有 GET、HEAD、POST、PUT、PATCH、DELETE、OPTIONS、TRACE ,而我们可以通过 method 属性来指定某个请求的具体请求方式。
1 2
| @RequestMapping(value = "/add/{a}/{b}",method = RequestMethod.POST)
|
1 2 3 4 5 6
| @GetMapping @PostMapping @PutMapping @DeleteMapping @PatchMapping
|
5. params 指定 URL 参数:
params 设置 URL 请求中是否必须包含或不能包含某些参数,不符合是 404 错误:
1 2 3 4 5 6
| @RequestMapping(value="hello02",params={"name"}) @RequestMapping(value="hello03",params={"!name"}) @RequestMapping(value="hello04",params={"name=4"})
@RequestMapping(value="hello04",params={"name!=4"}) @RequestMapping(value="hello05",params={"name!=5","age"})
|
使用规则与 params 属性设置的表达式类似,针对请求头的参数:
1 2
| headers = {"sec-ch-ua-platform=Windows"}
|
7. consumes 与 produces:
- consumes:只接受内容类型为指定值的请求,即规定请求头中的
Content-type
- produces:告诉浏览器返回的内容类型,即为响应头添加
Content-type:text/html;charset=utf-8
类似属性值。
转发与重定向:
1. 使用 servlet API :
1 2 3 4 5 6 7 8 9
| @RequestMapping("/hello") public void hello(HttpServletRequest req,HttpServletResponse rsp) throws ServletException, IOException { req.setAttribute("msg","hello springmvc"); req.getRequestDispatcher("/WEB-INF/jsp/hello.jsp").forward(req,rsp); }
|
2. 使用 springMVC:
带有 forward 和 redirect 关键字的前缀的字符串不会被视图解析器解析。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @RequestMapping("/hello") public String hello() {
return "redirect:/index.jsp"; }
|
请求处理:
1. 简单数据:
添加 @RequestParam 注解的变量默认该参数是必须的(可以为空字符串:&password=),参数不存在时报 404。
RequestParam 属性: defaultValue
为变量设置默认值, required
为 false 表示不是必须的(默认true)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
@RequestMapping("/hello")
public String hello(String user, @RequestParam("password")int pass, // 根据 key 获取指定参数 @RequestParam("pets")List<String> pets, // 一个 key 有多个值时可以封装为一个集合 // 拿到当前所有的参数(想拿到 pets 所有值使用 SpringMvc 工具类 MultiValueMap 替换 Map) @RequestParam Map<String,String> params, Model model) { String msg="用户名:"+user+",密码:"+pass; model.addAttribute("msg",msg); return "hello"; }
|
了解:POST、GET、@RequestBody 和 @RequestParam 区别
1 2 3 4
| public String hello04( @RequestHeader("User-Agent")String userAgent, // 根据 key 来获取请求头的信息 @RequestHeader Map<String,String> header ){}
|
1 2 3 4 5 6
| public String hello04(@CookieValue("JSESSIONID")String jId, @CookieValue("JSESSIONID") Cookie cookie){ System.out.println(cookie.getName()+"===>"+cookie.getValue()); }
|
1 2
| @DateTimeFormat(pattern = "yyyy-MM--dd") private Date date;
|
2. 映射对象:
首先建一个pojo类:
1 2 3 4 5 6 7 8
| public class User { private String name; private int pass; private Book book; }
class Book{ private String name; }
|
添加接受对象参数的方法:
1 2 3 4 5 6 7 8 9 10
|
@RequestMapping("/hello") public String hello(User user,String name, Model model) { model.addAttribute("msg",user+",name="+name); return "hello"; }
|
3. 原生 API :
springmvc 中允许直接在方法参数中添加原生的 API 参数:
1 2 3 4
| @RequestMapping("/add") public String add(HttpServletRequest request, HttpServletResponse response, HttpSession session){}
|
除上3个常用 api 以外还有一下 api 可用:
- java.security.Principal :与 https 安全相关
- Locale :国际化相关
数据回显:
1. 提供 AIP 参数:
在 springmvc 中除使用原生 API 将数据传递给页面以外,springmvc 中提供了 Map 、 Model 、 ModelMap 。springmvc 允许将它们添加在请求方法的参数列表中给我们使用:
1 2 3 4 5 6 7 8
| @RequestMapping("/test") public String test(Map<String,Object> map, Model model, ModelMap modelMap){ map.put("key","val"); model.addAttribute("key","val"); modelMap.addAttribute("key","val"); return "success"; }
|
它们添加的数据都被保存在 请求域(requestScope) 中,如在 jsp 页面我们通过 ${requestScope.key}
获取到数据。它们3都是利用了 BindingAwareModelMap 来实现数据的保存。
2. ModelAndView 返回值:
springmvc 中还提供了一种不在方法中添加参数,而是通过方法返回值返回 RequestMapping
对象的方式来传递数据。它的数据也一样被保存在 请求域(requestScope) 中。
1 2 3 4 5 6 7 8
| @RequestMapping("/test") public ModelAndView test(){ ModelAndView mv = new ModelAndView(); mv.setViewName("index"); mv.addObject("key","val"); return mv; }
|
3. 为 session 域添加数据:
SessionAttributes 将上方 Model 等保存的数据同时在 session域(sessionScope) 中保存,建议使用原生 API(HttpSession)。
1 2 3
|
@SessionAttributes(value = {"msg","key"} , types = {String.class})
|
4. 其它:
1. @ModelAttribute:
2. @RequestAttribute:
RequestAttribute
获取请求域中的信息:
1 2 3 4 5 6 7 8 9 10 11 12 13
| @GetMapping("/goToPage") public String goToPage(HttpServletRequest request){ request.setAttribute("msg","request msg"); return "forward:/success"; } @ResponseBody @GetMapping("/success") public String success( @RequestAttribute("msg")String msg){ return msg; }
|
乱码问题:
在 web.xml 中添加过滤器即可,springmvc 提供了字符编码过滤器,也可使用自定义的过滤器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <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> <init-param> <param-name>forceRequestEncoding</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>forceResponseEncoding</param-name> <param-value>true</param-value> </init-param> </filter>
<filter-mapping> <filter-name>characterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
|