SpringMVC

MVC是一种软件架构的思想,将软件按照模型、视图、控制器来划分。

M:model,模型层,指工程中的JavaBean,作用是处理数据

JavaBean分为两类:

实体类:一张表就是一个实体

业务处理类:service,dao

V:view,视图层,指工程中的html或jsp等页面,作用是与用户进行交互,展示数据

C:controller,控制层,指项目中的servlet,作用是接受请求和响应浏览器

MVC执行流程:用户通过视图层发送请求到服务器,在服务器中请求被Controller接收,Controller调用相应的Model层(Service->Dao)处理请求,处理完毕(Dao->Service)将结果返回到Controller,Controller再根据请求处理的结果找到相应的View视图,渲染数据后最终响应给浏览器

三层架构:

表现层:web层,负责接收客户端的请求,向客户端响应结果,通常客户端使用http协议请求web层,分为控制层(接收请求)和展示层(结果展示)

业务层:service层,负责业务逻辑的处理,和我们开发项目的需求息息相关

持久层:dao层,负责数据持久化,和数据库交互

springMVC位于项目的表现层

一、入门案例

流程总结:

1.创建maven工程,配置tomcat

2.导入pom依赖

3.在web-xml中注册前端控制器

4.创建springmvc配置文件,开启组件扫描,配置Thymeleaf视图编译器

5.创建controller类,加注解

6.创建html文件

*控制层中的方法都是通过转发进行请求访问的

*如果运行后显示的内容是index.jsp中的内容不是index.html中的内容,把index.jsp的名字改了就行

1.1 创建maven项目

1.2 添加依赖

XML 复制代码
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.13.2</version>
      <scope>test</scope>
    </dependency>
    <!--springMVC-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>6.1.14</version>
    </dependency>
    <!--日志-->
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.5.12</version>
    </dependency>
    <!--servletAPI-->
    <dependency>
      <groupId>jakarta.servlet</groupId>
      <artifactId>jakarta.servlet-api</artifactId>
      <version>6.1.0</version>
      <scope>provided</scope><!--服务器已提供,这个jar包不会存在在打包的war包中-->
    </dependency>
    <!--spring6和thymeleaf整合-->
    <dependency>
      <groupId>org.thymeleaf</groupId>
      <artifactId>thymeleaf-spring6</artifactId>
      <version>3.1.2.RELEASE</version>
    </dependency>
  </dependencies>

1.3 在web.xml中注册前端控制器

XML 复制代码
<!--配置springMVC的前端控制器,对浏览器发送的请求进行统一处理-->
  <servlet>
    <!--servlet-name和servet的名字保持一致-->
    <servlet-name>DispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!--配置springMVC配置文件的位置和名称-->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springMVC.xml</param-value>
    </init-param>
    <!--将前端控制器DispatcherServlet的初始化时间提前到服务器运行时-->
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <!--与上面的保持一致-->
    <servlet-name>DispatcherServlet</servlet-name>
    <!--
      /:所匹配的是所有情趣路径的请求但不包括.jsp的请求路径的请求
      /*:所匹配的是所有情趣路径的请求且包括.jsp的请求路径的请求
    -->
    <url-pattern>/</url-pattern>
  </servlet-mapping>

1.4 springMVC.xml中的配置

XML 复制代码
<?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"
       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">

    <!--1.开启注解扫描-->
    <context:component-scan base-package="com.yc"/>

    <!--2.配置thymeleaf视图解析器,下面是针对html进行的配置-->
    <bean id="viewResolver" class="org.thymeleaf.spring6.view.ThymeleafViewResolver">
        <!--设置视图解析器的优先级-->
        <property name="order" value="1"/>
        <!--设置解析视图时用的编码-->
        <property name="characterEncoding" value="utf-8"/>
        <!--模板-->
        <property name="templateEngine">
            <bean class="org.thymeleaf.spring6.SpringTemplateEngine">
                <property name="templateResolver">
                    <!--解析策略-->
                    <bean class="org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver">
                        <!--视图前缀-->
                        <!--
                            web-inf下的文件浏览器是不能直接访问的,重定向也是不能访问的,只能通过转发
                        -->
                        <property name="prefix" value="/WEB-INF/static/"/>
                        <!--视图后缀-->
                        <property name="suffix" value=".html"/>
                        <property name="templateMode" value="HTML"/>
                        <property name="characterEncoding" value="utf-8"/>
                    </bean>
                </property>
            </bean>
        </property>
    </bean>
</beans>

1.5 简单测试

在/WEB-INF/static/下创建index.html

在/main/java下创建一个类

html 复制代码
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>首页</title>
    </head>
    <body>
        <h1>首页</h1>
    </body>
</html>
java 复制代码
@Controller
public class HelloController {

    @RequestMapping("/") //请求映射
    public String index(){
        //返回视图名称---被视图解析器解析
        return "index";
    }
}

tomcat启动运行

1.6 访问目标页面

html 复制代码
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
    <!--
      @author:尤词
      @date:2024/10/30
      @description:
    -->
    <head>
        <meta charset="UTF-8">
        <title>首页</title>
    </head>
    <body>
        <h1>首页</h1>
        <!--th:href="@{/target}"自动加上上下文路径-->
        <a th:href="@{/target}">访问目标页面target.html</a>
    </body>
</html>
java 复制代码
    @RequestMapping("/target") //请求映射
    public String target(){
        //返回视图名称---被视图解析器解析
        return "target";
    }

同级目录下创建target.xml内容随便,仅做测试

1.7 小结

浏览器发送请求,若地址符合前端控制器的url-pattern,该请求就会被前端控制器DispatcherServlet处理。前端控制器会读取SpringMVC的核心配置文件,通过扫描组件找到控制器,将请求地址和控制器中@RequestMapping注解的value属性值进行匹配,若匹配成功,该注解所标识的控制器方法就是处理请求的方法。处理请求的方法需要返回一个字符串类型的视图名称,该视图名称会被视图解析器解析,加上前缀和后缀组成视图路径,通过Thymeleaf对视图进行渲染,最终转发到视图所对应页面。

二、@RequestMapping注解

功能:RequestMapping--请求映射,作用是将请求和处理请求的控制器方法关联起来,建立映射关系。

位置:

放在类上:设置映射请求的请求路径的初始信息

运用场景:如果有两个controller类,两个类中有同一个请求路径,那么将该注解加在类上做区分。因为请求路径必须唯一。

放在方法上:设置映射请求的请求路径的具体信息

证明路径必须唯一,在controller2这个类上加上@RequestMapping("/hello")注解

index.html中加一行

<a th:href="@{/target2}">访问目标页面target2.html</a>

controller2.Java中方法上的注解value变为traget2

测试是否能访问成功target2.html

说明Thymeleaf不会自动加类上的@RequestMapping中的初始路径,需要手动添加

@RequestMapping中的属性

String[] value():请求路径只要满足其中一个就可以访问成功,value可省略

java 复制代码
@RequestMapping(value = {"/target","/tar"}) //请求映射
    public String target(){
        //返回视图名称---被视图解析器解析
        return "target";
    }

(405)RequestMethod[] method():get/post/put/delete,匹配路径的同时匹配请求方式,可传多个值。如果匹配到了路径但是没匹配到请求方式,浏览器报405,不设置则任意请求方式都可以,method和value不可省略。在html中写个form表单post提交测试一下

java 复制代码
    @RequestMapping(value = {"/target","/tar"},method = {RequestMethod.POST,RequestMethod.GET}) //请求映射
    public String target(){
        //返回视图名称---被视图解析器解析
        return "target";
    }
html 复制代码
        <form th:action="@{/target}" method="post">
            <input type="submit" value="post请求">
        </form>

两者等价👇 ,post、put、delete同理

仅了解即可

(400)String[] params():通过请求的请求参数匹配请求映射,所有条件同时满足

匹配规则:

"param":要求请求映射所匹配的请求必须携带param请求参数

"!param":要求请求映射所匹配的请求必须不能携带param请求参数

"param=value":要求请求映射所匹配的请求必须携带param请求参数且param=value

"param!=value":要求请求映射所匹配的请求必须携带param请求参数但是param!=value

java 复制代码
    <body>
        <h1>首页</h1>
        <!--th:href="@{/target}"自动加上上下文路径-->
        <a th:href="@{/tar}">访问目标页面target.html</a>
        <form th:action="@{/tar(username)}" method="post"><!--传参方式-->
            <input type="submit" value="post请求">
        </form>
    </body>
java 复制代码
    //必须携带username参数
    @RequestMapping(value = "/tar",params = {"username"})
    public String target(){
        //返回视图名称---被视图解析器解析
        return "target";
    }
    //不许携带username参数
    @RequestMapping(value = "/tar",params = {"!username"})
    public String target(){
        //返回视图名称---被视图解析器解析
        return "target";
    }
    //username参数的值必须是admin
    @RequestMapping(value = "/tar",params = {"username=admin"})
    public String target(){
        //返回视图名称---被视图解析器解析
        return "target";
    }
    //username参数的值必须不是admin
    @RequestMapping(value = "/tar",params = {"username!=admin"})
    public String target(){
        //返回视图名称---被视图解析器解析
        return "target";
    }

仅了解即可

(404)String[] headers():通过请求的请求头信息匹配请求映射,匹配规则同params

String[] consumes():指定处理请求提交数据的类型(Content-Type),如果提交的数据不符合要求,报415不支持的媒体类型错误

String[] produces():设置响应类型和编码,即Content-Type

ant风格的路径

?:表示任意单个字符,访问的时候?可以时任意值(?和/不可以)

*:表示任意的0个或多个字符

**:表示任意的一层或多层目录----spring-webmvc5.3以上版本已弃用

注意:使用**时,只能使用/**/路径的方式

java 复制代码
    @RequestMapping("/a?a/testAnt")
    public String testAnt(){
        return "testAnt";
    }

    @RequestMapping("/a*a/testAnt")
    public String testAnt(){
        return "testAnt";
    }

    @RequestMapping("/**/testAnt")
    public String testAnt(){
        return "testAnt";
    }

★路径中的占位符

原始方式:/路径?属性名=属性值

restful方式:/路径/值

不能用request.getParameter取值

三、获取请求参数

3.1 通过servletAPI获取

java 复制代码
    @RequestMapping("/testServletAPI")
    //形参位置的request表示当前请求
    public String  getParam(HttpServletRequest  request){
        String name = request.getParameter("name");
        String password = request.getParameter("password");
        System.out.println("name= "+name+",password= "+password);
        return "testAnt";
    }
html 复制代码
<body>
    <h1>测试请求参数</h1>
    <a th:href="@{/testServletAPI(name='admin',password=123456)}">测试使用testServletAPI获取请求参数</a>
</body>

3.2 通过控制器放发的形参获取请求参数

html 复制代码
<body>
    <h1>测试请求参数</h1>
    <a th:href="@{/testServletAPI(name='admin',password=123456)}">测试使用testServletAPI获取请求参数</a>
    <a th:href="@{/testParam(name='admin',password=123456)}">测试使用控制器的形参获取请求参数</a>
</body>
java 复制代码
    @RequestMapping("/testParam")
    public String  getParam(String name,String password){
        System.out.println("name= "+name+",password= "+password);
        return "testAnt";
    }

如何获取同名参数(复选框)

如果报错IllegalArgumentException,在pom.xml中的build标签内添加:

XML 复制代码
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <parameters>true</parameters>
        </configuration>
      </plugin>
    </plugins>
java 复制代码
    @RequestMapping("/testParam")
    public String getParam(String name,String password,String hobby){
        System.out.println("name= "+name+",password= "+password +",hobby= "+ hobby);
        return "testAnt";
    }
    //两种都行
    @RequestMapping("/testParam")
    public String getParam(String name,String password,String[] hobby){
        System.out.println("name= "+name+",password= "+password +",hobby= "+ Arrays.toString(hobby));
        return "testAnt";
    }

假如页面中的名字带下划线

required :默认为true,表示该value对应的参数必须传参,如果不传参报400错误。设置为false如果形参没有传参就为类型的默认值。

java 复制代码
    @RequestMapping("/testParam")
    public String getParam(
            @RequestParam(value = "user_name",required = false,defaultValue = "默认值") String name,
            String password,
            String[] hobby,
            @RequestHeader("Host") String host,
            @CookieValue("JSESSIONID") String sessionId
    ){
        System.out.println("name= "+name+",password= "+password +",hobby= "+ Arrays.toString(hobby));
        System.out.println("host= "+host);
        System.out.println("sessionId= "+sessionId);
        return "testAnt";
    }

defaultValue: 设置没有传参时的默认值

3.3 @RequestParam

将请求参数和控制器方法的形参创建映射关系

属性:

value:指定为形参赋值的请求参数的参数名

required:设置是否必须传输次请求参数,默认值为true

defaultValue:不管required属性值为true还是false,当value所指定的请求参数没有传输或传输值为空时,则使用默认值为形参赋值。

3.4 @RequestHeader

将请求头信息和控制器方法的形参创建映射关系

属性同RequestParam

3.5 @CookieValue

将cookie数据和控制器方法的形参创建映射关系

属性同RequestParam

​​​​​​

3.6 通过POJO获取参数(实体类)

进行添加操作、或者更新操作时,页面中文本框较多,这些数据对应数据库中的一张表,表又对应一个实体类,可以在控制器方法的形参写一个实体类类型,前端页面传递的参数只需要和实体类的属性名相同即可完成把请求参数赋值给实体类

创建实体类

java 复制代码
package com.yc.pojo;

/**
 * @Author: 尤词
 * @Date: 2024/10/30 下午 10:43
 * @Description: 实现类
 */

public class User {
    private String username;
    private String password;
    private String sex;
    private int age;
    private String email;

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", sex='" + sex + '\'' +
                ", age=" + age +
                ", email='" + email + '\'' +
                '}';
    }

    public User() {
    }

    public User(String username, String password, String sex, int age, String email) {
        this.username = username;
        this.password = password;
        this.sex = sex;
        this.age = age;
        this.email = email;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

页面上写一个form表单

html 复制代码
        <form th:action="@{/testPojo}" method="get">
            用户名:<input type="text" name="username" placeholder="name">
            密码:<input type="text" name="password" placeholder="password">
            性别:<input type="radio" name="sex" value="male">男
                 <input type="radio" name="sex" value="female">女
            年龄:<input type="text" name="age" placeholder="age">
            邮箱:<input type="text" name="email" placeholder="email">
            <input type="submit" value="提交">
        </form>

controller中

java 复制代码
    @RequestMapping("/testPojo")
    public String getPojo(User user){
        System.out.println(user);
        return "testAnt";
    }

获取参数时出现中文乱码问题如何解决?

get请求的乱码是由tomcat造成的,需要在tomcat/conf/servlet.xml中设置,大概第70行

URIEncoding="UTF-8"

针对post请求获取参数中文乱码解决办法:在servlet执行前设置中文编码--->过滤器

在web.xml中注册过滤器(放在最前面)

XML 复制代码
  <!--过滤器-->
  <filter>
    <filter-name>encodingFilter</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>forceResponseEncoding</param-name>
      <param-value>true</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

四、域对象共享数据

4.1 request域

五种方式均是将我们的数据包装成ModelAndView

4.1.1 使用servletAPI向request域对象共享数据

创建success.html页面

html 复制代码
    <body>
        <!--获取request域对象中的参数-->
        获取request域对象中的参数:<p th:text="${name}"></p><br>
    </body>

在控制器中存储一个域对象,转发到success页面

java 复制代码
    @RequestMapping("/req")
    public String reqTest(HttpServletRequest request){
        request.setAttribute("name","yc");
        return "success";
    }

4.1.2 ★使用ModelAndView向request域对象共享数据

java 复制代码
    @RequestMapping("/mav")
    public ModelAndView mavTest(HttpServletRequest request){
        //创建ModelAndView对象
        ModelAndView mav = new ModelAndView();
        //处理模型数据,即向请求域request共享数据
        mav.addObject("name","mav 中的 yc");
        //设置视图名称,就是要跳转的页面去掉前缀和后缀剩下的部分
        mav.setViewName("success");
        return mav ;
    }

4.1.3 使用Model向request域对象共享数据

java 复制代码
    @RequestMapping("/model")
    public String modelTest(Model model){
        model.addAttribute("name", "model 中的 yc");
        return "success";
    }

4.1.4 使用map集合向request域对象共享数据

java 复制代码
    @RequestMapping("/map")
    public String mapTest(Map<String,Object> map){
        map.put("name","map 中的 yc");
        return "success";
    }

4.1.5 使用ModelMap集合向request域对象共享数据

java 复制代码
    @RequestMapping("/modelmap")
    public String ModelMapTest(ModelMap modelMap){
        modelMap.addAttribute("name","ModelMap 中的 yc");
        return "success";
    }

4.1.6 Model、ModelMap、Map的关系

三者的形参本质上都是BindingAwareModelMap

4.2 向session域对象共享数据

java 复制代码
    @RequestMapping("/session")
    public String SessionTest(HttpSession session){
        session.setAttribute("name","session 中的 yc");
        return "success";
    }
html 复制代码
    <body>
        <!--获取域对象中的参数-->
        获取session域对象中的参数:<p th:text="${session.name}"></p><br>
    </body>

4.3 向application域对象共享数据

就是servletContext域

html 复制代码
    <body>
        <!--获取域对象中的参数-->
        获取application域对象中的参数:<p th:text="${application.name}"></p><br>
    </body>
java 复制代码
    @RequestMapping("/application")
    public String ApplicationTest(HttpSession session){
        ServletContext application = session.getServletContext();
        application.setAttribute("name","application 中的 yc");
        return "success";
    }

五、SpringMVC的视图

SpringMVC中的视图时View接口,视图的作用是渲染数据,将模型中的数据展示给用户

SpringMVC视图的种类有很多,默认有转发的视图是InternalResourceView和重定向视图RedirectView

当工程引入jstl的依赖,转发视图会自动转换成jstlView

若使用的视图技术为Thymeleaf,在SpringMVC配置文件中配置了Thymeleaf视图解析器,由此视图解析器解析之后所得的是ThymeleafView

5.1 ThymeleafView

当控制器方法中所设置的视图名称没有任何前缀和后缀时,此时的视图名称会被SpringMVC配置文件中所配置的视图解析器解析,视图名称拼接视图前缀和视图后缀所得的最终路径,会通过转发的方式实现跳转,上面的return "html不带后缀的文件名"都是ThymeleafView。

5.2 转发视图

SpringMVC默认的转发视图时InteralResourceVIew:当控制器方法中所设置的视图名称以"forward:"为前缀时,创建InteralResourceVIew视图,此时的视图名称不会被SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀"forward:"去掉,剩余部分作为最终路径通过转发的方式实现跳转。

5.3 重定向视图

SpringMVC中默认的重定向视图是RedirectView:当控制器方法所设置的视图名称以"Redirect:"为前缀时,创建RedirectView视图,此时的视图名称不会被SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀"Redirect:"去掉,剩余部分作为最终路径通过重定向的方式实现跳转。

转发和重定向的区别:

转发是浏览器一次请求,第一次是浏览器请求,第二次是服务器内部的请求,地址栏不会变化,转发可以获取请求域对象中的数据,可以访问web-inf下的资源,不能跨域

重定向是浏览器两次请求,地址栏会变化,重定向不能获取请求域对象中的数据,不可以访问web-inf下的资源,可以跨域

java 复制代码
    @RequestMapping("redirect")
    public  String  Redtest(){
        //return后面的时视图名称,如果return想转发到一个请求,就按照如下写法,冒号后面写的是要转发的路径
        return "redirect:/view";
    }

5.4 视图控制器view-controller

当控制器方法中,仅仅用来实现页面跳转,即只需要设置视图名称时,可以将处理器方法使用view-controller标签进行表示。

path:设置请求地址

view-name:设置请求地址所对应的视图名称

SpringMVC控制文件中:

xmlns:mvc="http://www.springframework.org/schema/mvc"

http://www.springframework.org/schema/mvc

https://www.springframework.org/schema/mvc/spring-mvc.xsd

注意:当SpringMVC中设置任何一个view-controller时,其他控制器中的请求映射全部失效,此时需要在SpringMVC核心配置文件中设置开启mvc注解驱动的标签。

XML 复制代码
<mvc:view-controller path="/" view-name="index"/>
<!--开启mvc注解驱动-->
<mvc:annotation-driven/>

5.5 jsp

重新编写SpringMVC配置文件,

XML 复制代码
<!--1.开启注解扫描-->
<context:component-scan base-package="com.yc"/>
<!--针对jsp的视图解析器配置-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/dynamic/"/>
    <property name="suffix" value=".jsp"/>
</bean>

不需要写任何的跳转,启动tomcat服务器之后默认访问index.jsp,这个默认启动页是在tomcat的web.xml中配置好的,如果想改变默认启动页,可以在项目的web.xml中配置:

<welcome-file-list>

<welcome-file>index.html</welcome-file>

<welcome-file>index.jsp</welcome-file>

</welcome-file-list>

按照配置的顺序依次查找页面,匹配到一个符合要求的就停止查找并访问页面

html 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    <head>
        <title>Title</title>
    </head>
    <body>
        <h1>Hello World!</h1>
    </body>
</html>

在jsp中实现页面的转发

java 复制代码
@Controller
public class JspController {
    @RequestMapping("/success")
    public String success(){
        return "success";
    }
}

在WEB-INF/dynamic下创建success.jsp文件

html 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java"%>
<html>
    <head>
        <title>Title</title>
    </head>
    <body>
        <h1>Hello World!</h1>
        <%--动态获取上下文路径--%>
        <a href="${pageContext.request.contextPath}/success">success.jsp</a>
    </body>
</html>

如果地址栏出现乱码(如下图),说明页面中的el表达式被当成字符串处理了,做如下修改:

<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false"%>

六、RESTFul

资源状态转移,具体说,就是HTTP协议里面四个表示操作方式的动词:

GET:获取资源

POST:新建资源

PUT:更新资源

DELETE:删除资源

REST风格提倡URL地址使用统一的风格设计,从前到后各个单词使用斜杠分开,不使用问号键值对方式携带请求参数,而是将要发送给服务器的数据作为URL地址的一部分,以保证风格的一致性。

操作 传统方式 REST风格
查询操作 getUserById?id=1 user/1--->get请求方式
新增操作 SaveUser user--->post请求方式
删除操作 deleteUser?id=1 user/1--->delete请求方式
更新操作 updateUser user--->put请求方式

现在的项目web.xml如下

XML 复制代码
<?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">

    <!--1.开启注解扫描-->
    <context:component-scan base-package="com.yc"/>

    <!--2.配置thymeleaf视图解析器,下面是针对html进行的配置-->
    <bean id="viewResolver" class="org.thymeleaf.spring6.view.ThymeleafViewResolver">
        <!--设置视图解析器的优先级-->
        <property name="order" value="1"/>
        <!--设置解析视图时用的编码-->
        <property name="characterEncoding" value="UTF-8"/>
        <!--模板-->
        <property name="templateEngine">
            <bean class="org.thymeleaf.spring6.SpringTemplateEngine">
                <property name="templateResolver">
                    <!--解析策略-->
                    <bean class="org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver">
                        <!--视图前缀-->
                        <!--
                            web-inf下的文件浏览器是不能直接访问的,重定向也是不能访问的,只能通过转发
                        -->
                        <property name="prefix" value="/WEB-INF/static/"/>
                        <!--视图后缀-->
                        <property name="suffix" value=".html"/>
                        <property name="templateMode" value="HTML"/>
                        <property name="characterEncoding" value="UTF-8"/>
                    </bean>
                </property>
            </bean>
        </property>
    </bean>

    <!--针对jsp的视图解析器配置-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/dynamic/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <!--开启mvc注解驱动-->
    <mvc:annotation-driven/>
    <!--视图控制器-->
    <mvc:view-controller path="/" view-name="index"/>
    <mvc:view-controller path="/userTest" view-name="userTest"/>
</beans>

模拟user表的增删改查操作

java 复制代码
package com.yc.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

/**
 * @Author: 尤词
 * @Date: 2024/11/2 上午 12:12
 * @Description: user表的增删改查
 */
@Controller
public class UserController {
    /*
     * 使用RESTFul模拟用户资源的增删改查
     * /user           GET     查询所有用户信息
     *  /user/{id}     GET     查询单个用户信息
     *  /user          POST    新增用户信息
     *  /user/{id}     PUT     修改用户信息
     *  /user/{id}     DELETE  删除用户信息
     * */
    @RequestMapping(value = "/user", method = RequestMethod.GET)
    public String getAllUser() {
        System.out.println("查询所有用户信息");
        return "testAnt";
    }

    @RequestMapping(value = "/user/{id}",method = RequestMethod.GET)
    public String getUserById() {
        System.out.println("根据id查询用户信息");
        return "testAnt";
    }

    @RequestMapping(value = "/user",method = RequestMethod.POST)
    public String insertUser(String username,String password){
        System.out.println("添加用户信息:"+username+" "+password);
        return "testAnt";
    }
}
html 复制代码
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
    <!--
  @author:尤词
  @date:2024/11/2
  @description: 模拟user表的增删改查操作
-->
    <head>
        <meta charset="UTF-8">
        <title>user增删改查</title>
    </head>
    <body>
        <a th:href="@{/user}">查询所有用户信息</a><br>
        <a th:href="@{/user/1}">根据id查询用户信息</a>
        <form th:action="@{/user}" method="post">
            用户名:<input type="text" name="username" placeholder="username"><br>
            密码:<input type="text" name="password" placeholder="password"><br>
            <input type="submit" value="添加">
        </form>
    </body>
</html>

点击两个超链接

form表单提交数据

由于浏览器只支持发送get和post方式的请求,那么如何发送put和delete方式呢?

只能使用过滤器

在web.xml中配置过滤器:HiddenHttpMethodFilter,放在字符集过滤器下边

XML 复制代码
<!--配置处理请求方式的过滤器HiddenHttpMethodFilter-->
    <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>
html 复制代码
        <form th:action="@{/user}" method="post">
            <input type="hidden" name="_method" value="put">
            用户名:<input type="text" name="username" placeholder="username"><br>
            密码:<input type="text" name="password" placeholder="password"><br>
            <input type="submit" value="修改">
        </form>
java 复制代码
    @RequestMapping(value = "/user",method = RequestMethod.PUT)
    public String updateUserById(){
        System.out.println("根据id修改用户信息");
        return "testAnt";
    }

案例

模拟数据不连接数据库,在index.html中用这个超链接跳转全查页面

<a th:href="@{/user}">查看用户信息</a><br>

1.创建实体类

提供get、set、toString、全参构造、无参构造方法

java 复制代码
public class User {
    private Integer id;
    private String username;
    private String password;
    //1 男;2 女
    private Integer sex;
}

2.dao层

用集合模具数据库中的数据,SpringMVC配置文件和web.xml用上面的

java 复制代码
package com.yc.dao;

import com.yc.pojo.User;
import org.springframework.stereotype.Repository;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author: 尤词
 * @Date: 2024/11/2 上午 1:53
 * @Description: 模拟数据库中的数据
 */
@Repository
public class UserDao {
    private static Map<Integer, User> users = null;

    static {
        users = new HashMap<Integer, User>();
        users.put(1, new User(1, "zs", "zs123",1));
        users.put(2, new User(2, "ls", "ls123",0));
        users.put(3, new User(3, "ww", "ww123",1));
        users.put(4, new User(4, "zl", "zl123",0));
    }

    private static Integer initId = 5;

    //新增用户
    public void save(User user) {
        if (user.getId() == null)
            user.setId(initId++);
        users.put(user.getId(), user);
    }

    //全查
    public Collection<User> getAll() {
        return users.values();
    }

    //单查
    public User get(Integer id) {
        return users.get(id);
    }

    //删除
    public void delete(Integer id) {
        users.remove(id);
    }
}

3.controller层全部代码

java 复制代码
package com.yc.controller;

import com.yc.dao.UserDao;
import com.yc.pojo.User;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpRequest;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

import java.util.Collection;

/**
 * @Author: 尤词
 * @Date: 2024/11/2 上午 12:12
 * @Description: user表的增删改查
 */
@Controller
public class UserController {

    //依赖注入
    @Autowired
    private UserDao userdao;

    //全查
    @GetMapping(value = "/user")
    public String getAllUser(Model model) {
        System.out.println("查询所有用户信息");
        Collection<User> list = userdao.getAll();
        model.addAttribute("list", list);
        return "user_list";
    }

    //删除
    @RequestMapping(value = "/user/{id}", method = RequestMethod.DELETE)
    public String deleteUserById(@PathVariable("id") Integer id) {
        System.out.println("根据id删除用户信息");
        userdao.delete(id);
        //删除操作成功之后,就和之前的请求没有关系了,所以使用重定向到列表页面
        return "redirect:/user";
    }

    //跳转添加页面
    @RequestMapping(value = "/toAdd")
    public String toAdd() {
        System.out.println("跳转添加页面");
        return "user_add";
    }

    //处理添加业务
    @PostMapping("/user")
    public String addUser(User user) {
        System.out.println("添加用户信息:" + user);
        userdao.save(user);
        return "redirect:/user";
    }

    //跳转修改页面并渲染数据
    @GetMapping("/user/{id}")
    public String getUserById(@PathVariable("id") Integer id, Model model) {
         System.out.println("根据id查询用户信息");
        User user = userdao.get(id);
        model.addAttribute("user", user);
        return "user_update";
    }

    //处理修改业务
    @RequestMapping(value = "/user",method = RequestMethod.PUT)
    public String updateUser(User user){
        System.out.println("根据id修改用户信息");
        userdao.save(user);
        return "redirect:/user";
    }
}

4.页面设计

4.1 user_list.html
html 复制代码
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
    <!--
  @author:尤词
  @date:2024/11/2
  @description: 全查
-->
    <head>
        <meta charset="UTF-8">
        <title>用户信息表</title>
    </head>
    <body>
        <table id="dataTab" border="1" cellspacing="0" cellpadding="0" width="500" align="center">
            <tr>
                <th colspan="5">用户信息表</th>
            </tr>
            <tr>
                <th>id</th>
                <th>用户名</th>
                <th>密码</th>
                <th>性别</th>
                <th>操作(<a th:href="@{/toAdd}">添加</a>)</th>
            </tr>
            <tr th:each="user:${list}">
                <td th:text="${user.id}"></td>
                <td th:text="${user.username}"></td>
                <td th:text="${user.password}"></td>
                <td th:text="${user.sex}"></td>
                <td>
                    <a th:href="@{'/user/'+${user.id}}" @click="deleteUser">删除</a>
                    <a th:href="@{'/user/'+${user.id}}">修改</a>
                </td>
            </tr>
        </table>

        <form id="delForm" method="post">
            <input type="hidden" name="_method" value="DELETE">
        </form>

        <script type="text/javascript" th:src="@{/static/js/vue.js}"></script>
        <script>
            var vue = new Vue({
                el: "#dataTab",
                methods: {
                    deleteUser: function (event) {
                        //根据id获取form表单
                        var delForm = document.getElementById("delForm");
                        //将出发点击事件的超链接的href属性赋值给表单的action属性
                        delForm.action = event.target.href;
                        //提交表单
                        delForm.submit();
                        //取消超链接的默认行为
                        event.preventDefault();
                    }
                }
            });
        </script>

    </body>
</html>
4.2 user_add.html
html 复制代码
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
    <!--
  @author:尤词
  @date:2024/11/2
  @description: TODO
-->
    <head>
        <meta charset="UTF-8">
        <title>添加页面</title>
    </head>
    <body>
        <form th:action="@{/user}" method="post">
            用户名:<input type="text" name="username" placeholder="username">
            密码:<input type="text" name="password" placeholder="password">
            <input type="submit" value="submit">
        </form>
    </body>
</html>
4.3 user_update.html
html 复制代码
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
    <!--
  @author:尤词
  @date:2024/11/2
  @description: TODO
-->
    <head>
        <meta charset="UTF-8">
        <title>修改用户信息</title>
    </head>
    <body>
        <form th:action="@{/user}" method="post">
            <input type="hidden" name="_method" value="put">
            <input type="hidden" name="id" th:value="${user.id}">
            用户名:<input type="text" name="username" th:value="${user.username}"><br>
            密码:<input type="text" name="password" th:value="${user.password}"><br>
            性别:
            <input type="radio" name="sex" value="1" th:field="${user.sex}">男
            <input type="radio" name="sex" value="0" th:field="${user.sex}">女
            <br>
            <input type="submit" value="修改">
        </form>
    </body>
</html>

5.核心配置文件

XML 复制代码
<?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">

    <!--1.开启注解扫描-->
    <context:component-scan base-package="com.yc"/>

    <!--2.配置thymeleaf视图解析器,下面是针对html进行的配置-->
    <bean id="viewResolver" class="org.thymeleaf.spring6.view.ThymeleafViewResolver">
        <!--设置视图解析器的优先级-->
        <property name="order" value="1"/>
        <!--设置解析视图时用的编码-->
        <property name="characterEncoding" value="UTF-8"/>
        <!--模板-->
        <property name="templateEngine">
            <bean class="org.thymeleaf.spring6.SpringTemplateEngine">
                <property name="templateResolver">
                    <!--解析策略-->
                    <bean class="org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver">
                        <!--视图前缀-->
                        <!--
                            web-inf下的文件浏览器是不能直接访问的,重定向也是不能访问的,只能通过转发
                        -->
                        <property name="prefix" value="/WEB-INF/static/"/>
                        <!--视图后缀-->
                        <property name="suffix" value=".html"/>
                        <property name="templateMode" value="HTML"/>
                        <property name="characterEncoding" value="UTF-8"/>
                    </bean>
                </property>
            </bean>
        </property>
    </bean>

    <!--针对jsp的视图解析器配置-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/dynamic/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <!--配置视图控制器,只要页面由转发存在就要在这里配置-->
    <mvc:view-controller path="/" view-name="index"/>
    <mvc:view-controller path="/userTest" view-name="userTest"/>
    <mvc:view-controller path="/user_list" view-name="user_list"/>
    <mvc:view-controller path="/user_add" view-name="user_add"/>
    <mvc:view-controller path="/user_update" view-name="user_update"/>

    <!--开启mvc注解驱动-->
    <mvc:annotation-driven/>

    <!--开启对静态资源的访问-->
    <mvc:default-servlet-handler />
</beans>

6.处理静态资源流程

列表页面使用的是/user请求,请求方式为get;删除使用了vue.js,通过超链接控制表单的提交,给超链接绑定了点击事件,通过vue处理点击事件,在vue中组织超链接的默认行为,最终以提交表达的形式提交;添加用户跳转到添加页面;修改跳转到修改页面并把请求方式改为put提交。

如果项目报错vue.js找不到,1.重新打包war包 2.配置文件中配置开启对静态资源的访问

开启对静态资源的访问和开启mvc注解驱动要同时配置,不然当前项目的所有请求都由默认的servlet处理,那么除了静态资源其它请求都无法访问。

七、HttpMessageConverter

报文信息转换器,将轻微报文转换为java对象,将java对象转换为响应报文

HttpMessageConverter提供了两个注解和两个类型

@RequestBody @ResponseBody

RequestEntity ResponseEntity

7.1 @RequestBody

可以获取请求体,需要在控制器方法设置一个形参,使用@RequestBody进行标识,当前请求的请求体就会为当前注解所标识的参数赋值。

7.1.1 在index.html中写一个表单

html 复制代码
<form th:action="@{/testRequestBody}"  method="post">
    username:<input type="text"  name="username" ><br>
    password:<input type="text"  name="password" ><br>
    <input type="submit" value="testRequestBody">
</form>

7.1.2 controller

java 复制代码
@Controller
public class HttpController {
    //获取请求体
    @RequestMapping("/testRequestBody")
    public String getRequestBody(@RequestBody String body){
        System.out.println("body:"+body);
        return "testAnt";
    }
}

7.2 @ResponseBody

html 复制代码
<a th:href="@{/testResponseBody}">testResponseBody</a>
java 复制代码
    //responseBody
    @RequestMapping("/testResponseBody")
    @ResponseBody //加上这个注解之后,return后的字符串就不再是视图名称了,而是响应体
    public String getResponseBody(HttpServletResponse response) throws IOException {
        //原生响应 没有加@ResponseBody注解
        //response.getWriter().write("hello");
        //SpringMVC响应
        return "success";
    }

如果响应的是一个对象,是不能直接响应的,需要转换成json格式的字符串。

加入pom依赖

XML 复制代码
    <!--json-->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.18.1</version>
    </dependency>
java 复制代码
//响应的是一个对象---转换为json
    @RequestMapping("/testResponseBody")
    @ResponseBody
    public User getResponseBody2(){
        return new User(1001, "yc", "123456",1);
    }
SpringMVC处理Ajax
html 复制代码
<div id="app">
            <a th:href="@{/testAjax}" @click="testAxios">
                SpringMVC处理Ajax请求
            </a>
        </div>
        <script type="text/javascript" th:src="@{/static/js/axios.min.js}"></script>
        <script type="text/javascript" th:src="@{/static/js/vue.js}"></script>
        <script>
            new Vue({
                el: "#app",
                methods: {
                    testAxios:function (event) {
                        axios({
                            method:'post',
                            url:event.target.href,
                            params:{
                                username:'admin',
                                password:'123456'
                            }
                        }).then(function(response) {
                            alert(response.data)
                        });
                        event.preventDefault();
                    }
                }
            })
        </script>
java 复制代码
    //ajax
    @RequestMapping("/testAjax")
    @ResponseBody
    public String getAjax(String username,String password) {
        System.out.println("username:"+username);
        System.out.println("password:"+password);
        return "hello,axios";
    }

7.3 RequestEntity

封装请求报文的一种类型,需要在控制器方法的形参中设置该类型的形参,当前请求的请求报文就会赋值给该形参,可以通过getHeaders()获取请求头信息,通过getBody()获取请求体信息。

java 复制代码
//requestEntity
    @RequestMapping("/testRequestEntity")
    public String getRequestEntity(RequestEntity<String> requestEntity){
        //当前requestEntity表示整个请求报文的信息
        System.out.println("请求头:"+requestEntity.getHeaders());
        System.out.println("请求体:"+requestEntity.getBody());
        return "testAnt";
    }
html 复制代码
<form th:action="@{/testRequestEntity}" method="post">
    username:<input type="text" name="username"><br>
    password:<input type="text" name="password"><br>
    <input type="submit" value="testRequestBody">
</form>
派生注解@RestController

等于@ResponseBody+@Controller,相当于给当前控制器内的所有方法都加上了@ResponseBody注解。

7.4 ResponseEntity

用于控制器方法的返回值类型,该控制器方法的返回值就是响应到浏览器的响应报文。

八、文件上传和下载

使用ResponseEntity实现下载文件功能

java 复制代码
    @RequestMapping("/fileDown")
    public ResponseEntity<byte[]> download(HttpSession session) throws Exception {
        //获取servletContext对象--->当前整个工程
        ServletContext servletContext = session.getServletContext();
        //获取服务器中文件的真实路径
        String realPath = servletContext.getRealPath("/static/img/1.jpg");
        System.out.println("文件的真实路径为: "+realPath);
        //创建输入流--->文件不管上传还是下载都是通过流来实现的
        InputStream is = new FileInputStream(realPath);
        //获取文件的字节数组 is.available()--->获取输入流所对应的文件的所有字节数
        byte[] bytes = new byte[is.available()];
        //读取文件的字节数组
        is.read(bytes);
        //创建httpHeader对象设置响应头信息
        HttpHeaders headers = new HttpHeaders();
        //设置要下载的方式和下载文件的名称
        headers.add("Content-Disposition","attachment;filename=1.jpg");
        //设置响应状态码
        HttpStatus status = HttpStatus.OK;
        //创建ResponseEntity对象
        ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes,headers,status);
        //关闭流
        is.close();
        return responseEntity;
    }

文件的上传功能(servlet3.0)

添加依赖

XML 复制代码
<!--文件上传-->
    <dependency>
      <groupId>commons-fileupload</groupId>
      <artifactId>commons-fileupload</artifactId>
      <version>1.5</version>
    </dependency>
java 复制代码
@RequestMapping("/fileUpload")
    public String upLoad(MultipartFile photo,HttpSession session) throws Exception {
        //获取上传的文件名
        String filename = photo.getOriginalFilename();
        //获取后缀
        String suffix = filename.substring(filename.lastIndexOf("."));
        //UUID
        String uuid = UUID.randomUUID().toString();
        filename=uuid+suffix;
        //设置上传的路径
        String path = session.getServletContext().getRealPath("img");
        File file = new File(path);
        //判断文件夹是否存在,如果不存在就创建文件夹
        if(!file.exists()){
            file.mkdirs();
        }
        //文件的最终全路径
        String finalPath = path+File.separator+filename;
        photo.transferTo(new File(finalPath));
        return "testAnt";
    }

web.xml

XML 复制代码
<!--servlet-name和servet的名字保持一致-->
        <servlet-name>DispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--配置springMVC配置文件的位置和名称-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springMVC.xml</param-value>
        </init-param>
        <!--将前端控制器DispatcherServlet的初始化时间提前到服务器运行时-->
        <load-on-startup>1</load-on-startup>

        <multipart-config>
            <max-file-size>20971520</max-file-size>
            <max-request-size>20971520</max-request-size>
        </multipart-config>

    </servlet>
    <servlet-mapping>
        <!--与上面的保持一致-->
        <servlet-name>DispatcherServlet</servlet-name>
        <!--
          /:所匹配的是所有情趣路径的请求但不包括.jsp的请求路径的请求
          /*:所匹配的是所有情趣路径的请求且包括.jsp的请求路径的请求
        -->
        <url-pattern>/</url-pattern>
    </servlet-mapping>

核心配置文件添加

XML 复制代码
<!--文件上传解析器,将上传的文件封装为MultipartFile-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.support.StandardServletMultipartResolver"></bean>

页面

html 复制代码
        <form  method="post" enctype="multipart/form-data" th:action="@{/fileUpload}">
            头像:<input type="file"  name="photo" />
            <input type="submit"  value="上传" />
        </form>

九、拦截器

请求-->过滤器-->DispacherServlet-->pre-->controller-->PostHandle-->ModelAndView-->after

java 复制代码
package com.yc.Interceptor;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

/**
 * @Author: 尤词
 * @Date: 2024/11/3 下午 1:17
 * @Description: 第一个拦截器
 */
@Component
public class First implements HandlerInterceptor {
    //ctrl+o重写方法,在配置文件中注册拦截器
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //控制器方法执行之前执行,返回true放行,false拦截(就是不执行controller中的方法).
        System.out.println("===preHandle===");
        return HandlerInterceptor.super.preHandle(request, response, handler);
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        //控制器方法执行后执行
        System.out.println("===postHandle===");
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //处理完视图和模型数据,渲染视图完毕后执行
        System.out.println("===afterCompletion===");
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

一定要在配置文件中配置拦截器

java 复制代码
    <!--7.配置拦截器-->
    <mvc:interceptors>
        <!--<bean class="com.yc.Interceptor.First"/>所有请求都拦截-->
        <!--<ref bean="first"/>对所有请求都拦截-->
        <mvc:interceptor><!--指定拦截路径-->
            <mvc:mapping path="/**"/><!--拦截所有请求,/*只拦截上下文路径后的一层路径,如果是xxx/xx/xx就不拦截,/**是所有-->
            <mvc:exclude-mapping path="/"/><!--排除主页面的请求-->
            <ref bean="first"/>
        </mvc:interceptor>
    </mvc:interceptors>

十、异常处理器

10.1 基于配置的异常处理

XML 复制代码
    <!--8.配置异常处理-->
    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <property name="exceptionMappings">
            <props>
                <!--
                    properties的键表示处理器方法执行过程中出现的异常
                    properties的值表示若出现指定异常时,设置一个新的视图名称(报错之后你想让它去哪),跳转到指定页面
                    下面这个是精准匹配
                -->
                <prop key="java.lang.ArithmeticException">error</prop>
                <!--模糊匹配,出现异常先精准匹配,没有符合的才走模糊匹配-->
                <prop key="java.lang.Exception">error2</prop>
            </props>
        </property>
        <!--设置将异常信息共享在请求域中的键-->
        <property name="exceptionAttribute" value="ex"/>
    </bean>
java 复制代码
    @RequestMapping("/testExceptionHandler")
    public String testExceptionHandler(){
        System.out.println(1/0);
        return "testAnt";
    }
html 复制代码
    <head>
        <meta charset="UTF-8">
        <title>发生异常</title>
    </head>
    <body>
        <h1>ERROR</h1>
        <h2 th:text="${ex}"></h2>
    </body>

10.2 基于注解的异常处理

把配置文件中对异常处理器的配置注释掉,依旧访问/testExceptionHandler,效果一样

java 复制代码
@ControllerAdvice
public class ExceptionController {
    @ExceptionHandler(value = {ArithmeticException.class,NullPointerException.class})
    public String exceptionHandler(Exception e, Model model){
        model.addAttribute("ex",e);
        return "error";
    }
}

十一、注解配置SpringMVC

创建web容器启动配置类(替换掉web.xml)

创建SpringMVC配置类

十二、SpringMVC执行流程

12.1 常用组件

DispatcherServlet:前端控制器,不需要工程师开发,由框架提供

作用:统一处理请求和响应,整个流程控制中心,由它调用其他组件处理用户请求

HandlerMapping:处理器映射,不需要工程师开发,由框架提供

作用:根据url、method等信息查找Handler,即控制器方法

Handler(Controller):处理器,需要工程师开发

作用:在DispatcherServlet的控制下Handler对具体的用户请求进行处理

HandlerAdapter:处理器适配器,不需要工程师开发,由框架提供

作用:通过HandlerAdapter对处理器(控制器方法)进行执行

ViewResolver:视图解析器,不需要工程师开发,由框架提供

作用:进行视图解析,得到相应的视图,例如:thymeleafView,InternalResorceView,RedirectView

View:视图,不需要工程师开发,由框架提供

作用:将模型数据通过页面展示给用户

12.2 流程

1.用户向服务器发送请求--->DispatcherServlet

2.DispatcherServlet对请求的URL进行解析,得到请求资源标识符(URI),判断请求的URI对应的映射是否存在:

不存在:配置mvc:default-servlet-handler

存在则继续

3.根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain执行链对象的形式返回

4.DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter

5.如果成功获得HandlerAdapter,此时将开始执行拦截器的preHandler(...)方法【正向】

6.提取Request中的模型数据,填充Handler入参(形参),开始执行Handler(Controller)方法,处理请求。在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作

7.Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象

8.此时将开始执行拦截器的postHandle(...)方法【逆向】

9.根据返回的ModelAndView(此时会判断是否存在异常:如果存在异常,则执行10.HandlerExceptionResolver进行异常处理)选择一个适合的ViewResolver进行视图解析,11.根据Model和View,来渲染视图

12.渲染视图完毕执行拦截器的afterCompletion(...)方法【逆向】

13.将渲染结果返回给客户端

相关推荐
天天扭码7 分钟前
五天SpringCloud计划——DAY2之单体架构和微服务架构的选择和转换原则
java·spring cloud·微服务·架构
程序猿进阶7 分钟前
堆外内存泄露排查经历
java·jvm·后端·面试·性能优化·oom·内存泄露
FIN技术铺12 分钟前
Spring Boot框架Starter组件整理
java·spring boot·后端
小曲程序19 分钟前
vue3 封装request请求
java·前端·typescript·vue
陈王卜37 分钟前
django+boostrap实现发布博客权限控制
java·前端·django
小码的头发丝、37 分钟前
Spring Boot 注解
java·spring boot
java亮小白199742 分钟前
Spring循环依赖如何解决的?
java·后端·spring
飞滕人生TYF1 小时前
java Queue 详解
java·队列
武子康1 小时前
大数据-230 离线数仓 - ODS层的构建 Hive处理 UDF 与 SerDe 处理 与 当前总结
java·大数据·数据仓库·hive·hadoop·sql·hdfs
武子康1 小时前
大数据-231 离线数仓 - DWS 层、ADS 层的创建 Hive 执行脚本
java·大数据·数据仓库·hive·hadoop·mysql