目录
2、在 web.xml 中配置前端控制器 DispatcherServlet
6、@RequestMapping 中的占位符(@PathVariable 重点)
记住一句话:SpringMVC 封装了 Servlet。
(1)MVC
M:Model,模型层,指工程中的JavaBean,作用是处理数据
JavaBean分为两类:
V:View,视图层,指工程中的html或jsp等页面,作用是与用户进行交互,展示数据
C:Controller,控制层,指工程中的servlet,作用是接收请求和响应浏览器
(2)工作流程
用户通过视图层发送请求到服务器,在服务器中请求被 Controller 接收,Controller 调用相应的 Model 层处理请求,处理完毕将结果返回到 Controller,Controller 再根据请求处理的结果找到相应的 View 视图,渲染数据后最终响应给浏览器。
(1)三层架构
我们通过浏览器访问前端页面,前端页面通过异步提交的方式,发送请求到后端服务器,后端服务器采用 web层(servlet)、业务层(service)、数据层(dao)的三层架构形式进行开发,其中数据的交互使用 json 来传输。
(2)SpringMVC 是一种基于 Java 实现 MVC 模型的轻量级 Web 框架
(1)用户发送请求到前端控制器 DispatcherServlet
(2)DispatcherServlet 收到请求后调用处理器映射器 HandlerMapping
(3)DispatcherServlet 调用处理器适配器 HandlerAdapter
(3-1)HandlerAdapter 经过适配调用具体的 Controller 的方法;(Controller--> service --> Dao --> 数据库)
(3-2)HandlerAdapter 将 Controller 执行的结果 ModelAndView 返回给 DispatcherServlet
(4)DispatcherServlet 将执行的结果 ModelAndView 传给视图解析器 ViewReslover
(5)DispatcherServlet 根据 Model 对 View 进行渲染(将模型数据填充至视图中)
即将使用 Thymeleaf 视图模板技术,因此引入了相关依赖。
- <dependencies>
-
- <dependency>
- <groupId>javax.servletgroupId>
- <artifactId>javax.servlet-apiartifactId>
- <version>3.1.0version>
- <scope>providedscope>
- dependency>
-
- <dependency>
- <groupId>org.springframeworkgroupId>
- <artifactId>spring-webmvcartifactId>
- <version>5.3.1version>
- dependency>
-
-
- <dependency>
- <groupId>org.thymeleafgroupId>
- <artifactId>thymeleaf-spring5artifactId>
- <version>3.0.12.RELEASEversion>
- dependency>
- dependencies>
- <servlet>
- <servlet-name>DispatcherServletservlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
- servlet>
- <servlet-mapping>
- <servlet-name>DispatcherServletservlet-name>
- <url-pattern>/url-pattern>
- servlet-mapping>
(1)标签中使用 / 和 /* 的区别
JSP 是通过 Tomcat 的 JspServlet 来处理的,然后 JspServlet 将响应结果回传给页面,所以 DispatcherServlet 处理不了 jsp 页面。
(1)SpringMVC 的配置文件的默认地址和名称:
但实际开发中,通常将 SpringMVC 的配置文件放在 resource 目录下。
(2)Thymeleaf 模板文件路径
我们通常在 WEB-INF 目录下建立一个 templates 目录,用来存放视图模板。而访问 WEB-INF 目录下的资源,需要服务器端进行请求转发,所以逻辑视图就被用上了。
(3)配置文件
- "1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
-
-
- <bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
- <property name="order" value="1"/>
- <property name="characterEncoding" value="UTF-8"/>
- <property name="templateEngine">
- <bean class="org.thymeleaf.spring5.SpringTemplateEngine">
- <property name="templateResolver">
- <bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
-
- <property name="prefix" value="/WEB-INF/templates/"/>
-
- <property name="suffix" value=".html"/>
-
-
- <property name="templateMode" value="HTML5"/>
- <property name="characterEncoding" value="UTF-8" />
- bean>
- property>
- bean>
- property>
- bean>
-
- beans>
(1)@Controller
由于 DispatcherServlet 已经对浏览器发送的请求进行了统一的处理,所以我们不需要自己创建 Servlet 去处理请求。
但是具体的请求有不同的处理过程,因此需要创建处理具体请求的类,即请求控制器。
- package com.demo.controller;
-
- import org.springframework.stereotype.Controller;
-
- @Controller
- public class HelloController {
-
- }
SpringMVC 的控制器由一个 POJO(普通的Java类)担任,因此需要通过 @Controller 注解将其标识为一个控制层组件,交给 Spring 的 IOC 容器管理,此时 SpringMVC 才能够识别控制器的存在。
(2)@RequestMapping
请求映射:把浏览器发送的请求,映射到具体方法去处理。
(2-1)其中的 value 属性值,填写的是方法的路径:
当 value 的值,与浏览器发送的请求路径是一样的,说明这个方法就是用来处理该请求的方法。
(2-2)方法返回值
前面我们说到,服务器端进行请求转发需要用到逻辑视图,那么怎么用呢?
其实我们返回的字符串,就是所谓的逻辑视图。紧接着 web.xml 中配置的视图解析器,就会将这个返回值用视图前缀和视图后缀组装起来,最终形成了请求转发的地址。
- package com.demo.controller;
-
- import org.springframework.stereotype.Controller;
- import org.springframework.web.bind.annotation.RequestMapping;
-
- @Controller
- public class HelloController {
- @RequestMapping("/")
- public String index() {
- System.out.println("index");
- // 返回逻辑视图
- return "index";
- }
-
- @RequestMapping("/hello")
- public String hello() {
- return "hello";
- }
-
- }
注意:测试页面使用了 Thymeleaf 的语法。
(1)index.html
- html>
- <html lang="en" xmlns:th="http://www.thymeleaf.org">
- <head>
- <meta charset="UTF-8">
- <title>Titletitle>
- head>
- <body>
- <h1> index 页面 h1>
- <a th:href="@{/hello}"> 测试 SpringMVC a>
- <br/>
- <a href="/hello"> 测试绝对路径a>
- body>
- html>
(2)hello.html
- html>
- <html lang="en" xmlns:th="http://www.thymeleaf.org">
- <head>
- <meta charset="UTF-8">
- <title>Titletitle>
- head>
- <body>
- <h1> hello h1>
- body>
- html>
(3)项目结构
SpringMVC 访问不到 Controller,网上有很多解决方法,如果再尝试之后都没有办法解决(比如我),那么可以尝试删除当前工件,新建一个该项目的工件。
有关更多项目结构问题,可以查看:https://www.bilibili.com/video/BV1Ry4y1574R?p=15
按照其中的步骤,设置 web 目录即可。
将 DispatcherServlet-servlet.xml 文件,放到 resource 目录下,然后在 web.xml 文件中作如下修改:
- <servlet>
- <servlet-name>DispatcherServletservlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
- <init-param>
- <param-name>contextConfigLocationparam-name>
- <param-value>classpath:SpringMVC.xmlparam-value>
- init-param>
- <load-on-startup>1load-on-startup>
- servlet>
- <servlet-mapping>
- <servlet-name>DispatcherServletservlet-name>
- <url-pattern>/url-pattern>
- servlet-mapping>
(1)contextConfigLocation
这是正常项目中 Spring 配置文件放置在 resource 目录下的写法。
(2)
由于 DispatcherServlet 需要初始化的内容非常地多,因此将其放在服务器启动期间去初始化,可以节省页面访问的时间。
如下面代码所示:
- @Controller
- @RequestMapping("/manager")
- public class ManagerController {
- @RequestMapping("/user")
- public String user() {
- return "user";
- }
- }
请求路径为:
http://ip:port/工程路径/manager/user
一个 Servlet 可以处理多个请求(在 url-pattern 中设置),同样的,value 属性值为数组的时候,就可以接收多个请求。
- @Controller
- @RequestMapping("/manager")
- public class ManagerController {
- @RequestMapping({"/hello", "/also_hello"})
- public String hello() {
- return "hello";
- }
- }
若当前请求的请求地址满足请求映射的 value 属性,但是请求方式不满足 method 属性,则浏览器报错 405。
- @RequestMapping(
- value = {"/hello"},
- method = {RequestMethod.GET, RequestMethod.POST}
- )
- public String hello() {
- return "hello";
- }
(1)对于处理指定请求方式的控制器方法,SpringMVC中提供了@RequestMapping的派生注解
(2)常用的请求方式有get,post,put,delete
ant 风格的路径是指:
在 @RequestMapping 中,value 属性值可以包含上面三种格式。
- @RequestMapping("/a?a/test/ant")
- public String testAnt() {
- return "hello";
- }
- @RequestMapping("/a?a/*/ant")
- public String testAnt2() {
- return "hello";
- }
- @RequestMapping("**ant")
- public String testAnt3() {
- return "hello";
- }
以上代码,下面三种请求路径都能访问到 hello.html:
@RequestMapping 修饰的方法想要使用请求参数的方法有很多,其中一种就是在 @RequestMapping 的 value 属性中使用“占位符{ }”。
(1)书写格式
假设请求路径 /func 可以访问到目标方法,通常我们添加参数,请求路径会这么写:
/func?id=1&name=admin
而如果使用 @RequestMapping,那么应该这么写:
/func/1/admin
(2)对应地,在代码中要怎么获取到这两个参数呢?
- @RequestMapping("/func/{id}/{name}")
- public String func(@PathVariable("id") String id, @PathVariable("name") String name) {
- System.out.println("id: " + id);
- System.out.println("name: " + name);
- return "index";
- }
(3)请求路径:
(4)输出结果:
如果要使用 servlet 相关的方法,那么给方法的参数列表添加 request 等参数即可。
(1)测试代码
(1-1)controller(success.html 可以自己写)
- @RequestMapping("/login")
- public String login(HttpServletRequest req) {
- System.out.println("username: " + req.getParameter("username"));
- System.out.println("password: " + req.getParameter("password"));
- return "success"; // return 相当于请求转发
- }
(1-2)index.html
- <form th:action="@{/login}" method="post">
- <input type="text" name="username"/> <br/>
- <input type="password" name="password"/> <br/>
- <input type="submit" value="submit"/>
- form>
(2)输出结果
在控制器方法的形参位置,设置和请求参数同名的形参,当浏览器发送请求,匹配到请求映射时,在DispatcherServlet中就会将请求参数赋值给相应的形参。
(1)测试代码
(1-1)controller
- @RequestMapping("/login/param")
- public String loginParam(String username, String password) {
- System.out.println("username: " + username);
- System.out.println("password: " + password);
- return "success";
- }
(1-2)index.html(改了 action)
- <form th:action="@{/login/param}" method="post">
- <input type="text" name="username"/> <br/>
- <input type="password" name="password"/> <br/>
- <input type="submit" value="submit"/>
- form>
(2)输出结果
(3)@RequestParam
为了保证一定能获取到对应值,可以使用 @RequestParam:
public String loginParam(@RequestParam(value = "username", required = true) String username, String password)
(4)@RequestHeader
用法与 @RequestParam 一致。
- @RequestMapping("/login/param")
- public String loginParam(@RequestParam(value = "username") String name,
- String password,
- @RequestHeader(value = "referer") String refer) {
- System.out.println("username: " + name);
- System.out.println("password: " + password);
- System.out.println("referer: " + refer);
- return "success";
- }
(5)@CookieValue
用法与 @RequestParam 一致。
- @RequestMapping("/login")
- public String login(HttpServletRequest req) {
- HttpSession session = req.getSession();
- System.out.println("创建 JSESSIONID: " + session.getId());
- return "index"; // return 相当于请求转发
- }
-
- @RequestMapping("/login/param")
- public String loginParam(@RequestParam(value = "username") String name,
- String password,
- @RequestHeader(value = "referer") String refer,
- @CookieValue(value = "JSESSIONID") String sessionId) {
- System.out.println("username: " + name);
- System.out.println("password: " + password);
- System.out.println("referer: " + refer);
- System.out.println("JSESSIONID: " + sessionId);
- return "success";
- }
(5-1)先访问 /login,使服务器创建 JSESSIONID
(5-2)输入 username、password,点击登录,观察控制台输出
通常情况下,我们会有非常多的方法参数需要设置/获取请求参数值,如果都用 @RequestParam 这几个注解来获取,也非常麻烦。
(1)解决方法
- @RequestMapping("/login/pojo")
- public String pojo(User user) {
- System.out.println(user);
- return "success";
- }
(2) 输出结果
因为没有 request 和 response,所以无法用这两个变量设置。
并且就算使用 request 和 response,其实也无法设置,因为其他参数都是已经从请求中获取到了,再设置也没有用了。
注意:
(1)解决方法
解决获取请求参数的乱码问题,可以使用 SpringMVC 提供的编码过滤器 CharacterEncodingFilter,再所有 servlet 处理之前,拦截所有请求,然后设置编码。
需要注意的是,CharacterEncodingFilter 必须在 web.xml 中进行注册:
(2)web.xml 配置文件
- <filter>
- <filter-name>CharacterEncodingFilterfilter-name>
- <filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
- <init-param>
- <param-name>encodingparam-name>
- <param-value>UTF-8param-value>
- init-param>
- <init-param>
- <param-name>forceResponseEncodingparam-name>
- <param-value>trueparam-value>
- init-param>
- filter>
- <filter-mapping>
- <filter-name>CharacterEncodingFilterfilter-name>
- <url-pattern>/*url-pattern>
- filter-mapping>
(3)输出结果