学习笔记:黑马程序员SpringMVC

文章目录

1.概述

笔记小结:

  • 含义:SpringMVC:Spring MVC是Spring Framework中的一个模块,用于构建基于Java的Web应用程序。SpringMVC是一种表现层框架技术、SpringMVC用于进行表现层功能开发
  • MVC:设计模式,它将应用程序分为三个主要组件:模型(Model)、视图(View)和控制器(Controller)
  • 特点:
    1. 轻量级
    2. 松耦合
    3. 高度可定制
    4. 易于测试
    5. 集成其他技术
    6. RESTful支持

1.1含义

​ Spring MVC是Spring Framework中的一个模块,用于构建基于Java的Web应用程序。它提供了一种基于MVC(Model-View-Controller)设计模式的开发方式,帮助开发人员构建灵活、可扩展的Web应用程序。

​ Spring MVC通过将请求和响应分派给相应的处理器(Controller)来处理Web请求,并将处理结果呈现给用户。它采用了松耦合的设计,允许开发人员将应用程序的业务逻辑和用户界面分开,提供了更好的代码组织和可维护性。

image-20230814221014724

说明:

  • SpringMVC是一种基于Java实现MVC模型的轻量级Web框架

  • 优点

    • 使用简单、开发便捷(相比于Servlet)
    • 灵活性强
  • SpringMVC,替换了后端服务器的表现层的Servlet开发

image-20230527102146297

说明:

​ SpringMVC的表现成方式,相比于Servlet开发更加简介

1.2MVC设计模式

image-20230814102502858

​ MVC是一种软件设计模式,它将应用程序分为三个主要组件:模型(Model)、视图(View)和控制器(Controller)。这种模式的目标是将应用程序的逻辑、数据和表示分离,以提高代码的可维护性重用性灵活性

  • 模型(Model):模型表示应用程序的数据和业务逻辑。它负责处理数据的读取、存储、验证以及处理业务规则。模型不直接与视图进行交互,而是通过控制器来传递数据
  • 视图(View):视图负责展示模型的数据给用户。它通常是用户界面的一部分,可以是网页、图形界面、命令行界面等。视图接收来自控制器的数据并将其呈现给用户
  • 控制器(Controller):控制器处理用户的请求并相应地更新模型和视图。它接收用户输入(例如点击按钮、提交表单等),根据输入调用适当的模型逻辑来更新数据,并将更新后的数据发送给视图进行显示

​ MVC模式的核心思想是将应用程序的不同功能部分分离,使其各自独立并且可替换。这种分离使得代码易于理解维护扩展。它也促进了团队合作,因为开发人员可以同时工作在模型、视图和控制器的不同部分上,而不会互相干扰。MVC模式被广泛应用于各种软件开发框架和技术中,包括Web开发框架如Spring MVC、Ruby on Rails等。

1.3SpringMVC特点

​ Spring MVC是一个基于Java的Web框架,它是Spring框架的一部分,用于开发灵活、可扩展的Web应用程序。以下是Spring MVC的一些特点:

  1. 轻量级:Spring MVC采用了基于POJO的开发模式,不依赖于任何特定的应用服务器,因此非常轻量级。

  2. 松耦合:Spring MVC与Spring框架紧密集成,利用了Spring的依赖注入和面向切面编程等特性,实现了组件之间的松耦合。

  3. 高度可定制:Spring MVC提供了丰富的配置选项和可扩展的接口,开发人员可以根据需求自定义和配置各种组件,以满足特定的应用程序要求。

  4. 易于测试:Spring MVC采用了MVC设计模式,将业务逻辑、数据处理和用户界面分离,使得单元测试和集成测试变得更加容易。

  5. 强大的请求处理:Spring MVC提供了灵活且强大的请求处理机制,通过注解和配置文件可以轻松地定义请求映射、参数绑定、数据校验、视图解析等。

  6. 视图技术多样性:Spring MVC支持多种视图技术,包括JSP、Thymeleaf、Freemarker、Velocity等,开发人员可以根据项目需求选择合适的视图技术。

  7. 集成其他技术:Spring MVC可以与其他技术和框架无缝集成,如Spring Security实现安全认证和授权、Spring Data访问数据库、Spring Boot简化配置等。

  8. RESTful支持:Spring MVC对RESTful风格的开发提供了良好的支持,可以轻松地创建和管理RESTful风格的API。

总体而言,Spring MVC具有灵活性、可定制性和可测试性等优势,使得它成为开发Web应用程序的首选框架之一。

2.基本用例-SpringMVC基础应用

笔记小结:

  • 操作步骤
    1. 导入servlet-apispring-webmvc依赖坐标
    2. 创建SpringMVC控制器类(等同于Servlet功能)
    3. 创建配置文件类
    4. 初始化Servlet容器,用于加载SpringMVC环境,并设置SpringMVC技术处理的请求:
      • 说明:继承AbstractDispatcherServletInitializer此类、重写三个抽象方法、通过AnnotationConfigWebApplicationContext来加载SpringMVC控制器的文件
  • 原理:
    • image-20230814221025847
    • 详细请看下一节
  • 注解使用:
    • @Controller:设定SpringMVC的核心控制器bean
    • @RequestMapping:设置当前控制器方法请求访问路径
    • @ResponseBody:设置当前控制器方法响应内容为当前返回值,无需解析
    • @Configuration:标识一个类是 Spring 配置类,通常配合其他注解一起使用,用于定义应用程序的配置信息
    • @ComponentScan:告诉 Spring 在指定的包中扫描组件(包括类、接口、注解等),并将它们注册为 Spring 的 Bean

步骤一:使用SpringMVC技术需要先导入SpringMVC坐标与Servlet坐标

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.10.RELEASE</version>
</dependency>

说明:

  • SpringMVC坐标,可以使用Spring MVC提供的功能和特性,如请求处理、路由、数据绑定、视图解析
  • 通过引入Servlet坐标,可以将Servlet容器的功能集成到项目中,从而使得项目能够处理HTTP请求生成响应

步骤二:创建SpringMVC控制器类(等同于Servlet功能)

//定义表现层控制器bean
@Controller
public class UserController {
    
    

    //设置映射路径为/save,即外部访问路径
    @RequestMapping("/save")
    //设置当前操作返回结果为指定json数据(本质上是一个字符串信息)
    @ResponseBody
    public String save() {
    
    
        System.out.println("user save ...");
        return "{'info':'springmvc'}";
    }

    //设置映射路径为/delete,即外部访问路径
    @RequestMapping("/delete")
    @ResponseBody
    public String delete() {
    
    
        System.out.println("user save ...");
        return "{'info':'springmvc'}";
    }
}

补充:

  1. 名称:@Controller

    • 类型:类注解

    • 位置:SpringMVC控制器类定义上方

    • 作用:设定SpringMVC的核心控制器bean

    • 范例:

      @Controller
      public class UserController {
               
               
      }
      
      
  2. 名称:@RequestMapping

    • 类型:方法注解

    • 位置:SpringMVC控制器方法定义上方

    • 作用:设置当前控制器方法请求访问路径

    • 范例:

      @RequestMapping("/save")
      public void save(){
               
               
          System.out.println("user save ...");
      }
      
    • 相关属性

      • value(默认):请求访问路径
  3. 名称:@ResponseBody

    • 类型:方法注解

    • 位置:SpringMVC控制器方法定义上方

    • 作用:设置当前控制器方法响应内容为当前返回值,无需解析

    • 范例:

      @RequestMapping("/save")
      @ResponseBody
      public String save(){
               
               
          System.out.println("user save ...");
          return "{'info':'springmvc'}";
      }
      

步骤三:创建配置文件类

//springmvc配置类,本质上还是一个spring配置类
@Configuration
@ComponentScan("love.ljtxy.controller")
public class SpringMvcConfig {
    
    
}

说明:

​ 初始化SpringMVC环境(同Spring环境),设定SpringMVC加载对应的bean

注意:

​ 此处只需要将controller层变为Bean即可,不需要多余设备扫描包

步骤四:初始化Servlet容器,用于加载SpringMVC环境,并设置SpringMVC技术处理的请求

//web容器配置类
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
    
    
    //加载springmvc配置类,产生springmvc容器(本质还是spring容器)
    protected WebApplicationContext createServletApplicationContext() {
    
    
        //初始化WebApplicationContext对象
        AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
        //加载指定配置类
        ctx.register(SpringMvcConfig.class);
        return ctx;
    }

    //设置由springmvc控制器处理的请求映射路径
    protected String[] getServletMappings() {
    
    
        return new String[]{
    
    "/"};
    }

    //加载spring配置类
    protected WebApplicationContext createRootApplicationContext() {
    
    
        return null;
    }
}

说明:

​ 定义一个servlet 容器启动的配置类,在里面加载spring 的配置

3.流程原理

笔记小结:

  1. 启动服务器初始化过程
  2. Web容器初始化
  3. Servlet容器初始化
  4. SpringMVC配置类加载
  5. 加载@ComponentScan对应的Bean
  6. 控制层Bean加载
  7. 请求处理

image-20230814221054845

  1. 单次请求过程

  2. 请求

  3. 求分发

  4. 求解析

  5. 法分发

  6. 法执行

  7. 法返回处理

  8. 注解使用:

  • @ComponentScan:自动扫描指定包及其子包中的类,将其识别为Spring组件。依赖注入,自动装配
    • excludeFilters:排除扫描路径中加载的bean,需要指定类别(type)与具体项(classes)
    • includeFilters:加载指定的bean,需要指定类别(type)与具体项(classes)

3.1概述

​ SpringMVC启动里,流程大致分为两种,一种叫启动服务器初始化过程,另一种叫单次请求过程。

  • 启动服务器初始化过程
    1. 服务器启动,执行ServletContainersInitConfig类,初始化web容器
    2. 执行createServletApplicationContext方法,创建了WebApplicationContext对象
    3. 加载SpringMvcConfig
    4. 执行@ComponentScan加载对应的bean
    5. 加载UserController,每个@RequestMapping的名称对应一个具体的方法
    6. 执行getServletMappings方法,定义所有的请求都通过SpringMVC
  • 单次请求过程
    1. 发送请求localhost/save
    2. web容器发现所有请求都经过SpringMVC,将请求交给SpringMVC处理
    3. 解析请求路径/save
    4. 由/save匹配执行对应的方法save()
    5. 执行save()
    6. 检测到有@ResponseBody直接将save()方法的返回值作为响应求体返回给请求方

image-20230814103709269

说明:

​ 创建的WebApplicationContext对象,会被放在大的Servelet请求的ServletContext中

3.2SpringMVC与Spring的bean加载控制

​ 在com.itheima包下,包含了各种Bean,例如config、controller等等。而SpringMVC仅控制表现成Controller层的开发,Spring控制config、、service、dao层的开发。但是,此时将Spring的包扫描范围指定为com.itheima包下,则会导致Spring误加载SpringMVC的bean

image-20230530073206117

说明:

​ 加载Spring控制的bean的时候排除掉SpringMVC控制的bean,从而解决因为功能不同,如何避免Spring错误的加载到SpringMVC的Bea

​ 补充:

  • SpringMVC相关bean(表现层bean)
  • Spring控制的bean
    • 业务bean(Service)
    • 功能bean(DataSource等)
  • SpringMVC相关bean加载控制
    • SpringMVC加载的bean对应的包均在com.itheima.controller包内
  • Spring相关bean加载控制
    • 方式一:Spring加载的bean设定扫描范围为com.itheima,排除controller包内的bean
    • 方式二:Spring加载的bean设定扫描范围为精准范围,例如service包、dao包等
    • 方式三:不区分Spring与SpringMVC的环境,加载到同一个环境中

3.3ServletContainersInitConfig.class

/**
 * Web容器配置类
 */
public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
    
    

    /**
     * 返回Spring应用程序的根配置类
     */
    @Override
    protected Class<?>[] getRootConfigClasses() {
    
    
        return new Class[]{
    
    SpringConfig.class};
    }

    /**
     * 返回Servlet的配置类
     */
    @Override
    protected Class<?>[] getServletConfigClasses() {
    
    
        return new Class[]{
    
    SpringMvcConfig.class};
    }

    /**
     * 返回DispatcherServlet的映射路径
     */
    @Override
    protected String[] getServletMappings() {
    
    
        return new String[]{
    
    "/"};
    }
}

注意:

​ 使用getServletConfigClasses方法,需要配置对应的返回值为SpringMvcConfig.class,是处于控制层的配置

3.4SpringConfig.class

@Configuration
@ComponentScan(value = "love.ljtxy",
        excludeFilters = @ComponentScan.Filter(
                type = FilterType.ANNOTATION,
                classes = Controller.class
        ))
public class SpringConfig {
    
    
}

说明:

  • 名称:@ComponentScan
  • 类型:类注解
  • 属性
    • excludeFilters:排除扫描路径中加载的bean,需要指定类别(type)与具体项(classes)
    • includeFilters:加载定的bean,需要指定类别(type)与具体项(classes)

细节:

​ 因为此注解@Filter,处于 @ComponentScan中,因此可以直接用内部注解的方式.出来

3.5SpringMvcConfig.class

//springmvc配置类,本质上还是一个spring配置类
@Configuration
@ComponentScan("love.ljtxy.controller")
public class SpringMvcConfig {
    
    

}

说明:

​ 此处只需要将SpringMVC的控制配置为仅扫描controller层的包,即可。因为,SpringMVC的配置,替换了Spring中的控制层

4.请求与响应

笔记小结:

  1. 请求映射路径:@RequestMapping:设置当前控制器方法请求访问路径,如果设置在类上统一设置当前控制器方法请求访问路径前缀

  2. 请求方式:

  • Get请求传参
  • Post请求参数
  1. Post请求中文乱码处理
// AbstractAnnotationConfigDispatcherServletInitializer 是 Spring Framework 中的一个类,它用于配置和初始化基于注解的 Spring MVC 应用程序
public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer{
     
     
    // 配字符编码过滤器
    protected Filter[] getServletFilters() {
     
     
        CharacterEncodingFilter filter = new CharacterEncodingFilter(); //添加过滤器
        filter.setEncoding("utf-8"); //指定过滤器字符集
        return new Filter[]{
     
     filter};
    }
}
  1. 请求参数

  2. 普通参数-名相同无需配置

  3. 普通参数-名不同:需要使用**@RequestParam绑定参数关系**

  4. POJO参数(重点):定义POJO类型形参即可直接接收参数,无需额外配置,注意实体类中的属性名需要对应一致。适用于常规的表单参数绑定!

  5. 嵌套POJO参数:请求参数名与形参对象属性名相同,按照对象层次结构关系即可接收嵌套POJO属性参数

  6. 数组参数:定义数组类型形参即可接收多个参数。适用于常规的表单参数绑定!

  7. 集合保存普通参数:@RequestParam绑定参数关系,例如@RequestParam List<String> likes

  8. 传递Json数据

    1. 导入坐标:
    <dependency>
     <groupId>com.fasterxml.jackson.core</groupId>
     <artifactId>jackson-databind</artifactId>
     <version>2.9.0</version>
    </dependency>
    
    1. 设置请求参数

    2. 开启自动转换json数据的支持

    @Configuration
    @ComponentScan("com.itheima.controller")
    @EnableWebMvc // 在SpringMVC中需要开启Json数据自动类型转换,与SpringBoot不同
    public class SpringMvcConfig {
           
           
    }
    
  9. 传递Json对象:定义POJO类型形参,并使用注解@RequestBody即可接收参数。处理 JSON 数据时使用!!

  10. 传递Json数组:json数组数据与集合泛型属性名相同,定义List类型形参即可接收参数。处理 JSON 数据时使用!!

  11. 日期类型参数传递:

  • @DateTimeFormat(pattern = “yyyy-MM-dd”) :设定日期时间型数据格式
  1. 注解解释:
  • @RequestParam绑定请求参数与处理器方法形参间的关系,常用于表单之间的数据处理
  • @EnableWebMvc:开启SpringMVC多项辅助功能
  • @RequestBody:将请求中请求体所包含的数据传递给请求参数,此注解一个处理器方法只能使用一次。常用于Json之间的数据处理
  1. 响应:
  • 响应页面: return “page.jsp”;
  • 响应数据:
    • 文本数据:return “response text”;
    • Json数据:使用**@ResponseBody**,将响应设置为Json格式。其底层是运用类型转换器HttpMessageConverter接口来对应不同实现类,进而进行转换

4.1请求映射路径

@Controller
@RequestMapping("/user")
public class UserController {
    
    
    @RequestMapping("/save")
    @ResponseBody
    public String save(){
    
    
        System.out.println("user save ...");
        return "{'module':'user save'}";
    }
}

说明:

名称:@RequestMapping

  • 类型:方法注解 类注解

  • 位置:SpringMVC控制器方法定义上方

  • 作用:设置当前控制器方法请求访问路径,如果设置在类上统一设置当前控制器方法请求访问路径前缀

  • 属性

    • value(默认):请求访问路径,或访问路径前缀

4.2请求方式

1.Get请求传参

image-20230530082533259

2.Post请求参数

image-20230530082552044

4.3Post请求中文乱码处理

// AbstractAnnotationConfigDispatcherServletInitializer 是 Spring Framework 中的一个类,它用于配置和初始化基于注解的 Spring MVC 应用程序
public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer{
    
    
    // 配字符编码过滤器
    protected Filter[] getServletFilters() {
    
    
        CharacterEncodingFilter filter = new CharacterEncodingFilter(); //添加过滤器
        filter.setEncoding("utf-8"); //指定过滤器字符集
        return new Filter[]{
    
    filter};
    }
}

说明:

​ 需要在SpringMVC中添加过滤器,就像Servlet请求中的一样,需要为响应请求添加请求字符的过滤器

细节:

​ 为web容器添加过滤器并指定字符集,Spring-web包中提供了专用的字符过滤器

补充:

更多详细参考:AbstractAnnotationConfigDispatcherServletInitializer剖析_焱魔王的博客-CSDN博客

4.4请求参数

​ 请求参数是客户端(浏览器、移动应用等)发送给服务器的数据,用于告诉服务器要执行的操作或提供所需的数据。

4.4.1普通参数-名相同

​ url地址传参,地址参数名与形参变量名相同,定义形参即可接收参数

image-20230530083541992

@RequestMapping("/commonParam")
@ResponseBody
public String commonParam(String name ,int age){
    
    
    System.out.println("普通参数传递 name ==> "+name);
    System.out.println("普通参数传递 age ==> "+age);
    return "{'module':'common param'}";
}

4.4.2普通参数-名不同

​ 请求参数名与形参变量名不同,使用@RequestParam绑定参数关系

image-20230530083618242

@RequestMapping("/commonParamDifferentName")
@ResponseBody
public String commonParamDifferentName(@RequestParam("name")String userName , int age){
    
    
    System.out.println("普通参数传递 userName ==> "+userName);
    System.out.println("普通参数传递 age ==> "+age);
    return "{'module':'common param different name'}";
}

细节:

​ 若请求参数名与形参变量名不同,需要使用@RequestParam进行参数关系的绑定。若相同,则无需指定

补充:

  • 名称:@RequestParam
    • 类型:形参注解
    • 位置:SpringMVC控制器方法形参定义前面
    • 作用:绑定请求参数与处理器方法形参间的关系
  • 参数:
    • required:是否为必传参数
    • defaultValue:参数默认值

4.4.3POJO参数(重点)

​ 请求参数名与形参对象属性名相同,定义POJO类型形参即可接收参数。适用于常规的表单参数绑定

image-20230530083936860

@RequestMapping("/pojoParam")
@ResponseBody
public String pojoParam(User user){
    
    
    System.out.println("pojo参数传递 user ==> "+user);
    return "{'module':'pojo param'}";
}

说明:

​ 当使用PoJo参数传递时,值需要传递的参数与对象中的属性名称对应则可以接收

细节:

​ 当使用PoJo参数传递参数时,SpringMVC框架首先会根据PoJo的类型获得构造方法,先进行创建对象,后根据参数的set方法进行传递

4.4.4嵌套POJO参数

​ 请求参数名与形参对象属性名相同,按照对象层次结构关系即可接收嵌套POJO属性参数

image-20230530084152345

@RequestMapping("/pojoContainPojoParam")
@ResponseBody
public String pojoContainPojoParam(User user){
    
    
    System.out.println("pojo嵌套pojo参数传递 user ==> "+user);
    return "{'module':'pojo contain pojo param'}";
}

4.4.5数组参数

​ 请求参数名与形参对象属性名相同且请求参数为多个,定义数组类型形参即可接收参数

image-20230530084413567

@RequestMapping("/arrayParam")
@ResponseBody
public String arrayParam(String[] likes){
    
    
    System.out.println("数组参数传递 likes ==> "+ Arrays.toString(likes));
    return "{'module':'array param'}";
}

4.4.6集合保存普通参数

​ 请求参数名与形参集合对象名相同且请求参数为多个,@RequestParam绑定参数关系

image-20230530084449696

@RequestMapping("/listParam")
@ResponseBody
public String listParam(@RequestParam List<String> likes){
    
    
    System.out.println("集合参数传递 likes ==> "+ likes);
    return "{'module':'list param'}";
}

注意:

​ 若使用集合保存普通参数时,SpringMVC框架会将此集合当成PoJo对象进行传递。而集合又没有构造方法,因此会报错

image-20230530085326930

​ 此时,加上@RequestParam注解后,SpringMVC框架会直接将请求中涉及到的参数放入集合中

4.4.7基本用例-传递Json数据步骤

步骤一:添加json数据转换相关坐标

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.9.0</version>
</dependency>

步骤二:设置发送json数据(请求body中添加json数据)

image-20230530090141109

步骤三:开启自动转换json数据的支持

@Configuration
@ComponentScan("com.itheima.controller")
@EnableWebMvc // 在SpringMVC中需要开启Json数据自动类型转换,与SpringBoot不同
public class SpringMvcConfig {
    
    
}

说明:

​ @EnableWebMvc注解功能强大,该注解整合了多个功能,此处仅使用其中一部分功能,即json数据进行自动类型转换

补充:

  • 名称:@EnableWebMvc
    • 类型:配置类注解
    • 位置:SpringMVC配置类定义上方
    • 作用:开启SpringMVC多项辅助功能

步骤四:设置接收json数据

@RequestMapping("/listParamForJson")
@ResponseBody
public String listParamForJson(@RequestBody List<String> likes){
    
    
    System.out.println("list common(json)参数传递 list ==> "+likes);
    return "{'module':'list common for json param'}";
}

说明:

名称:@RequestBody

  • 类型:形参注解
  • 位置:SpringMVC控制器方法形参定义前面
  • 作用:将请求中请求体所包含的数据传递给请求参数,此注解一个处理器方法只能使用一次

4.4.8传递Json对象(重点)

​ json数据与形参对象属性名相同,定义POJO类型形参即可接收参数。处理 JSON 数据时使用

image-20230530090604255

前提:

  • 添加json数据转换相关坐标
  • 开启自动转换json数据的支持
@RequestMapping("/pojoParamForJson")
@ResponseBody
public String pojoParamForJson(@RequestBody User user){
    
    
    System.out.println("pojo(json)参数传递 user ==> "+user);
    return "{'module':'pojo for json param'}";
}

4.4.9传递Json数组

​ json数组数据与集合泛型属性名相同,定义List类型形参即可接收参数

image-20230530090734676

@RequestMapping("/listPojoParamForJson")
@ResponseBody
public String listPojoParamForJson(@RequestBody List<User> list){
    
    
    System.out.println("list pojo(json)参数传递 list ==> "+list);
    return "{'module':'list pojo for json param'}";
}

注意:需要有如下前提

  • 添加json数据转换相关坐标
  • 开启自动转换json数据的支持

4.5日期类型参数传递

  • 日期类型数据基于系统不同格式也不尽相同
    • 2088-08-18
    • 2088/08/18
    • 08/18/2088
  • 接收形参时,根据不同的日期格式设置不同的接收方式
@RequestMapping("/dataParam")
@ResponseBody
public String dataParam(Date date,
                        @DateTimeFormat(pattern = "yyyy-MM-dd") Date date1,
                        @DateTimeFormat(pattern = "yyyy/MM/dd HH:mm:ss")Date date2){
    
    
    System.out.println("参数传递 date ==> "+date);
    System.out.println("参数传递 date(yyyy-MM-dd) ==> "+date1);
    System.out.println("参数传递 date(yyyy/MM/dd HH:mm:ss) ==> "+date2);
    return "{'module':'data param'}";
}

说明:

  • 请求:

    http://localhost/dataParam?date=2088/08/08&date1=2088-08-18&date2=2088/08/28 8:08:08
    

补充:

  • 名称:@DateTimeFormat
    • 类型:形参注解
    • 位置:SpringMVC控制器方法形参前面
    • 作用:设定日期时间型数据格式
    • 属性:
      • pattern:日期时间格式字符串

注意:需要在配置类上开启类型转换功能

  • 名称:@EnableWebMvc

  • 作用:根据类型匹配对应的类型转换器是此注解的功能之一

扩展:通过注解来进行参数的传递,内部的工作其实是通过Converter接口来进行实现

  • Converter接口

    public interface Converter<S, T> {
           
           
        @Nullable
        T convert(S var1);
    }
    
  • 请求参数年龄数据(String→Integer)

  • json数据转对象(json → POJO)

  • 日期格式转换(String → Date)

image-20230530141442994

Convert有如此多的实现类,将数据类型进行转换

4.6响应

4.6.1概述

​ 响应则是服务器向客户端返回的消息,用于提供请求所需的数据或执行操作的结果

响应分类:

  1. 响应页面
  2. 响应数据
    • 文本数据
    • json数据

4.6.2响应页面(了解)

@RequestMapping("/toPage")
public String toPage(){
    
    
    return "page.jsp";
}

4.6.3响应文本数据(了解)

@RequestMapping("/toText")
@ResponseBody
public String toText(){
    
    
    return "response text";
}

补充:

  • 名称:@ResponseBody
    • 类型:方法注解
    • 位置:SpringMVC控制器方法定义上方
    • 作用:设置当前控制器返回值作为响应体

注意:

​ 使用@ResponseBody进行响应内容时,需要添加

4.6.4响应Json数据(对象转Json)

@RequestMapping("/toJsonPOJO")
@ResponseBody
public User toJsonPOJO(){
    
    
    User user = new User();
    user.setName("赵云");
    user.setAge(41);
    return user;
}

补充:

  • 类型转换器HttpMessageConverter接口

    public interface HttpMessageConverter<T> {
           
           
       boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);
       boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);
       List<MediaType> getSupportedMediaTypes();
       T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
             throws IOException, HttpMessageNotReadableException;
       void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
             throws IOException, HttpMessageNotWritableException;
    }
    

    ​ 当使用@ResponseBody注解,进行内容的响应时,并不是通过Converter 接口来进行转换,而是通过全新的接口HttpMessageConverter接口来进行转换。

4.6.5响应Json数据(对象集合转Json数组)

@RequestMapping("/toJsonList")
@ResponseBody
public List<User> toJsonList(){
    
    
    User user1 = new User();
    user1.setName("赵云");
    user1.setAge(41);
    User user2 = new User();
    user2.setName("master 赵云");
    user2.setAge(40);
    List<User> userList = new ArrayList<User>();
    userList.add(user1);
    userList.add(user2);
    return userList;
}

5.REST风格

笔记小结:

  1. 概述:REST(Representational State Transfer)是一种软件架构风格,用于关注资源的状态资源间的交互

  2. 设定HTTP请求动作(动词):@RequestMapping(value = “xxx”, method = RequestMethod.POST)

  3. 设定请求参数(路径变量):@RequestMapping(value = “/users/{id}” ,method = xxxx)

  4. RESTful快速开发:

    • @RestController:设置当前控制器类为RESTful风格,等同于@Controller与@ResponseBody两个注解组合功能

      @RestController
      public class BookController {
               
               
      }
      
    • @GetMapping @PostMapping @PutMapping @DeleteMapping:设置当前控制器方法请求访问路径请求动作

      @GetMapping("/{id}")
      public String getById(@PathVariable Integer id){
               
               }
      

5.1概述

​ REST(Representational State Transfer)是一种软件架构风格,用于设计网络应用程序的分布式系统。它主要关注资源的状态资源之间互动,通过使用统一的接口和HTTP协议的各种方法来实现。

  • 传统风格资源描述形式

    http://localhost/user/getById?id=1

    http://localhost/user/saveUser

  • REST风格描述形式

    http://localhost/user/1

    http://localhost/user

优点:

  • 隐藏资源的访问行为,无法通过地址得知对资源是何种操作
  • 书写简化

REST风格简介

image-20230530150231874

5.2设定HTTP请求动作(动词)

@RequestMapping(value = "/users", method = RequestMethod.POST)
@ResponseBody
public String save(@RequestBody User user){
    
    
    System.out.println("user save..." + user);
    return "{'module':'user save'}";
}

@RequestMapping(value = "/users" ,method = RequestMethod.PUT)
@ResponseBody
public String update(@RequestBody User user){
    
    
    System.out.println("user update..."+user);
    return "{'module':'user update'}";
}

补充:

  • 名称:@RequestMapping
    • 类型:方法注解
    • 位置:SpringMVC控制器方法定义上方
    • 作用:设置当前控制器方法请求访问路径
    • 属性
      • value(默认):请求访问路径
      • method:http请求动作,标准动作(GET/POST/PUT/DELETE)

5.3设定请求参数(路径变量)

@RequestMapping(value = "/users/{id}" ,method = RequestMethod.DELETE)
@ResponseBody
public String delete(@PathVariable Integer id){
    
    
    System.out.println("user delete..." + id);
    return "{'module':'user delete'}";
}

补充:

  • 名称:@PathVariable
    • 类型:形参注解
    • 位置:SpringMVC控制器方法形参定义前面
    • 作用:绑定路径参数与处理器方法形参间的关系,要求路径参数名与形参名一一对应

5.4RESTful快速开发

名称:@RestController

  • 类型:类注解

  • 位置:基于SpringMVC的RESTful开发控制器类定义上方

  • 作用:设置当前控制器类为RESTful风格,等同于@Controller与@ResponseBody两个注解组合功能

  • 范例:

    @RestController
    public class BookController {
          
          
    }
    

名称:@GetMapping @PostMapping @PutMapping @DeleteMapping

  • 类型:方法注解

  • 位置:基于SpringMVC的RESTful开发控制器方法定义上方

  • 作用:设置当前控制器方法请求访问路径与请求动作,每种对应一个请求动作,例如@GetMapping对应GET请求

  • 范例:

    @GetMapping("/{id}")
    public String getById(@PathVariable Integer id){
          
          
        System.out.println("book getById..."+id);
        return "{'module':'book getById'}";
    }
    
  • 属性:value(默认):请求访问路径

5.5基本案例-基于RESTful页面数据交互

步骤零:基础环境搭建

image-20230530194836763

步骤一:制作SpringMVC控制器,并通过PostMan测试接口功能

说明:

​ 跟基本用例-SpringMVC基础应用一样流程,此处不再进行展示

步骤二:设置对静态资源的访问放行

@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
    
    
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
    
    
        //当访问/pages/????时候,走/pages目录下的内容
        registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
        registry.addResourceHandler("/js/**").addResourceLocations("/js/");
        registry.addResourceHandler("/css/**").addResourceLocations("/css/");
        registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
    }
}

说明:

​ 当访问网页资源时,因为在步骤零中的ServletContainersInitConfig类配置了getServletMappings方法为拦截/跟路径,因此访问webapp目录下的静态资源时,会出现请求被SpringMVC拦截情况。

protected String[] getServletMappings() {
     
     
return new String[]{
     
     "/"};
}

​ 此时,处理方法有很多,可以新建一个类,继承WebMvcConfigurationSupport然后重写addResourceHandlers方法来添加资源处理器。在对资源处理时,将访问的/xx/**路径下的资源进行/xx/资源目录的访问即可

注意:

​ 当配置了此请求,必须要SpringMVC来加载此配置

步骤三:加载资源请求配置

//springmvc配置类,本质上还是一个spring配置类
@Configuration
@ComponentScan({
    
    "love.ljtxy.controller","love.ljtxy.config"})
@EnableWebMvc
public class SpringMvcConfig {
    
    

}

说明:

​ 此时,添加新的扫描类即可

6.SSM整合(重点)

笔记小结:此节为重点,详细请查看各个小节

6.1概述

笔记小结:

  • 含义:SSM框架是Spring、Spring MVC 、和Mybatis框架的整合,是标准的MVC模式。标准的SSM框架有四层,分别是dao层(mapper),service层,controller层和View层。

  • 持久层:dao层(mapper)层,主要是做数据持久层的工作,负责与数据库进行联络的一些任务都封装在此。

  • 业务层:Service层,Service层主要负责业务模块的逻辑应用设计。

  • 表现层:Controller层(Handler层),负责具体的业务模块流程的控制。

  • 视图层:View层,主要和控制层紧密结合,主要负责前台jsp页面的表示

  • 关系交互图:

    image-20230601092944922

​ SSM框架是spring、spring MVC 、和mybatis框架的整合,是标准的MVC模式。标准的SSM框架有四层,分别是dao层(mapper),service层,controller层和View层。使用spring实现业务对象管理,使用spring MVC负责请求的转发和视图管理,mybatis作为数据对象的持久化引擎。

1)持久层:dao层(mapper)层
作用:主要是做数据持久层的工作,负责与数据库进行联络的一些任务都封装在此。

  • Dao层首先设计的是接口,然后再Spring的配置文件中定义接口的实现类。
  • 然后可以在模块中进行接口的调用来进行数据业务的处理。(不在关心接口的实现类是哪个类)
  • 数据源的配置以及有关数据库连接的参数都在Spring的配置文件中进行配置。

2)业务层:Service层
作用:Service层主要负责业务模块的逻辑应用设计。

  • 先设计接口然后再设计实类,然后再在Spring的配置文件中配置其实现的关联。(业务逻辑层的实现具体要调用到自己已经定义好的Dao的接口上)这样就可以在应用中调用Service接口来进行业务处理。
  • 建立好Dao之后再建立service层,service层又要在controller层之下,因为既要调用Dao层的接口又要提供接口给controller层。每个模型都有一个service接口,每个接口分别封装各自的业务处理的方法。

3)表现层:Controller层(Handler层)
作用:负责具体的业务模块流程的控制。

  • 配置也同样是在Spring的配置文件里面进行,
  • 调用Service层提供的接口来控制业务流程。
  • 业务流程的不同会有不同的控制器,在具体的开发中可以将我们的流程进行抽象的归纳,设计出可以重复利用的子单元流程模块。

4)View层
作用:主要和控制层紧密结合,主要负责前台jsp页面的表示。

6.2整合配置

笔记小结:

  • 步骤一:导入依赖
  • 步骤二:创建基础目录结构
  • 步骤三:编写JdbcConfig类
    • 创建DruidDataSource作为数据源
    • 创建DataSourceTransactionManager作为事务管理器
  • 步骤四:编写MyBatisConfig类
    • 配置SqlSessionFactoryBean,用于创建SqlSessionFactory实例
    • 配置MapperScannerConfigurer,用于自动扫描并注册MyBatis的Mapper接口
  • 步骤五:编写ServletConfig类
    • 获取Spring的根配置类,用于配置Spring容器
    • 获取SpringMVC的配置类,用于配置SpringMVC容器
    • 获取DispatcherServlet的映射路径
  • 步骤六:编写SpringConfig类
    • 扫描指定包下的组件和Bean
    • 加载jdbc.properties属性文件
    • 导入JdbcConfig和MyBatisConfig配置类
    • 开启事务
  • 步骤七:编写SpringMvcConfig类
    • 扫描控制器组件所在的包
    • 启用Spring MVC功能

详细请查看本小节

步骤一:导入依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.10.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.2.10.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.2.10.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.6</version>
</dependency>

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>1.3.0</version>
</dependency>

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.47</version>
</dependency>

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.16</version>
</dependency>

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
    <scope>provided</scope>
</dependency>

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.15.0</version>
</dependency>

说明:

​ 这些都是一些常规的SSM整合依赖,其中javax.servlet-api为Servlet请求交互依赖,spring-webmvc为控制层替换依赖,mybatis和mybatis-spring为SpringMVC整合MyBatis依赖,druid和spring-jdbc用于Spring提供数据源操作依赖,jackson-core用于响应数据与接收数据的转换

步骤二:创建基础目录结构

image-20230531074500917

步骤三:编写JdbcConfig类

public class JdbcConfig {
    
    
    // 注入jdbc.driver配置项的值
    @Value("${jdbc.driver}")
    private String driver;
    // 注入jdbc.url配置项的值
    @Value("${jdbc.url}")
    private String url;
    // 注入jdbc.username配置项的值
    @Value("${jdbc.username}")
    private String username;
    // 注入jdbc.password配置项的值
    @Value("${jdbc.password}")
    private String password;

    @Bean
    public DataSource dataSource() {
    
    
        // 创建DruidDataSource作为数据源
        DruidDataSource dataSource = new DruidDataSource();
        // 配置数据源的驱动类
        dataSource.setDriverClassName(driver);
        // 配置数据源的URL
        dataSource.setUrl(url);
        // 配置数据源的用户名
        dataSource.setUsername(username);
        // 配置数据源的密码
        dataSource.setPassword(password);
        return dataSource;
    }

    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
    
    
        // 创建DataSourceTransactionManager作为事务管理器
        DataSourceTransactionManager ds = new DataSourceTransactionManager();
        // 设置事务管理器使用的数据源
        ds.setDataSource(dataSource);
        return ds;
    }
}

说明:

​ 通过以上的注入操作,可以将配置文件中的数据库连接相关的属性值注入到相应的字段中。这样,其他需要使用数据源的组件可以通过依赖注入的方式获取到这个数据源Bean,从而获取数据库连接进行操作,事务管理。

步骤四:编写MyBatisConfig类

public class MyBatisConfig {
    
    

    /**
     * 配置SqlSessionFactoryBean,用于创建SqlSessionFactory实例
     * @param dataSource 数据源
     * @return SqlSessionFactoryBean实例
     */
    @Bean
    public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) {
    
    
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        factoryBean.setTypeAliasesPackage("love.ljtxy.entity"); // 设置实体类的别名包路径
        return factoryBean;
    }

    /**
     * 配置MapperScannerConfigurer,用于自动扫描并注册MyBatis的Mapper接口
     * @return MapperScannerConfigurer实例
     */
    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer() {
    
    
        MapperScannerConfigurer msc = new MapperScannerConfigurer();
        msc.setBasePackage("love.ljtxy.mapper"); // 设置Mapper接口的包路径
        return msc;
    }
}

说明:

​ 通过在Spring的配置文件中引入该配置类,即可完成MyBatis的相关配置,使得应用程序可以方便地使用MyBatis进行数据库操作。

步骤五:编写ServletConfig类

public class ServletConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
    
    

    /**
     * 获取Spring的根配置类,用于配置Spring容器
     * @return 根配置类数组
     */
    protected Class<?>[] getRootConfigClasses() {
    
    
        return new Class[]{
    
    SpringConfig.class};
    }

    /**
     * 获取SpringMVC的配置类,用于配置SpringMVC容器
     * @return SpringMVC配置类数组
     */
    protected Class<?>[] getServletConfigClasses() {
    
    
        return new Class[]{
    
    SpringMvcConfig.class};
    }

    /**
     * 获取DispatcherServlet的映射路径
     * @return 映射路径数组
     */
    protected String[] getServletMappings() {
    
    
        return new String[]{
    
    "/"};
    }
}

说明:

​ 通过在Web容器启动时,自动加载该Servlet配置类,可以完成Spring和SpringMVC容器的初始化和配置,使得应用程序可以处理Web请求并进行相应的处理。

补充:

​ SpringMVC的容器,能访问Spring容器。而Spring的容器,不能访问SpringMVC的容器。因为此处设置了Spring 的IOC容器为Spring MVC 的IOC容器的父容器

image-20230602093019060

步骤六:编写SpringConfig类

@Configuration
@ComponentScan("love.ljtxy.service") // 扫描指定包下的组件和Bean
@PropertySource("classpath:jdbc.properties") // 加载jdbc.properties属性文件
@Import({
    
    JdbcConfig.class, MyBatisConfig.class}) // 导入JdbcConfig和MyBatisConfig配置类
@EnableTransactionManagement //开启事务
public class SpringConfig {
    
    
}

注意:

​ 当Spring使用了事务时,需要使用注解@EnableTransactionManagement来开启事务

说明:

​ 通过这些配置,Spring容器可以加载和管理指定包路径下的组件和Bean,并且可以根据需要导入其他的配置类,实现灵活的配置和组装。

补充:

  • @Import注解是Spring Framework中的一个元注解,用于将其他配置类导入到当前配置类中,以便组合多个配置类的功能。通过@Import注解,可以将其他配置类作为Bean定义加载到Spring容器中。
  • 使用 @PropertySource 注解可以方便地管理和加载属性文件中的属性值,使得配置类中的属性值更加灵活和可配置。

步骤七:编写SpringMvcConfig类

@Configuration
@ComponentScan("love.ljtxy.controller") // 扫描控制器组件所在的包
@EnableWebMvc // 启用Spring MVC功能
public class SpringMvcConfig {
    
    
}

说明:

​ 通过以上配置,Spring MVC能够自动扫描指定包下的控制器类,并提供基于注解的请求映射、参数解析、视图解析等功能,使得开发Web应用更加方便和高效。

6.3功能模块开发

笔记小结:

  • 步骤一:编写实体类
  • 步骤二:编写Dao层接口
    • @Insert@Update@Delete@Select 注解将 SQL 语句与方法关联起来。
    • 注解中的 SQL 语句使用占位符 #{propertyName} 来引用对象的属性值,实现动态参数绑定。
  • 步骤三:编写Service接口
    • 通常来说,Service接口层的编写,需要见名知意思
    • 通常来说,在编写接口时,需要对此服务的操作结果进行返回
  • 步骤四:编写Service接口实现层
    • 通常来说,编写完Service实现类后,就可以对Service层进行方法的测试
  • 步骤五:测试Service层
    • @RunWith注解用于指定测试运行器(test runner),它告诉JUnit在运行测试时使用特定的运行器来执行测试。
    • @ContextConfiguration注解用于指定Spring容器的配置信息,告诉JUnit在运行测试之前要加载哪些配置。
  • 编写表现层

步骤一:编写实体类

@Date
public class Book {
    
    
    private Integer id;
    private String type;
    private String name;
    private String description;
}

步骤二:编写Dao层接口

public interface BookDao {
    
    

    // 插入书籍
    @Insert("insert into tbl_book (type, name, description) values (#{type}, #{name}, #{description})")
    public void save(Book book);

    // 更新书籍
    @Update("update tbl_book set type = #{type}, name = #{name}, description = #{description} where id = #{id}")
    public void update(Book book);

    // 删除书籍
    @Delete("delete from tbl_book where id = #{id}")
    public void delete(Integer id);

    // 根据ID查询书籍
    @Select("select * from tbl_book where id = #{id}")
    public Book getById(Integer id);

    // 获取所有书籍
    @Select("select * from tbl_book")
    public List<Book> getAll();
}

说明:

  • 这些方法使用了 MyBatis 的注解方式来指定对应的 SQL 语句,通过 @Insert@Update@Delete@Select 注解将 SQL 语句与方法关联起来。注解中的 SQL 语句使用占位符 #{propertyName} 来引用对象的属性值,实现动态参数绑定。
  • 使用这些方法可以方便地进行数据库操作,例如插入书籍、更新书籍信息、删除书籍、查询单个书籍和获取所有书籍列表。

步骤三:编写Service接口

@Transactional
public interface BookService {
    
    

    /**
     * 保存
     *
     * @param book
     * @return
     */
    public boolean saveBookInfo(Book book);

    /**
     * 修改
     *
     * @param book
     * @return
     */
    public boolean updateBookInfo(Book book);

    /**
     * 按id删除
     *
     * @param id
     * @return
     */
    public boolean deleteBookInfo(Integer id);

    /**
     * 按id查询
     *
     * @param id
     * @return
     */
    public Book getBookInfo(Integer id);

    /**
     * 查询全部
     *
     * @return
     */
    public List<Book> getBookAllInfo();
}

注意:

  		当Spring使用了事务时,需要使用注解`@Transactional`来启用事务。若还需要对@Transactional做其余的配置,可以根据需求来进行决定

说明:

  • 通常来说,Service接口层的编写,需要见名知意思
  • 通常来说,在编写接口时,需要对此服务的操作结果进行返回

步骤四:编写Service接口实现层

@Service
public class BookServiceImpl implements BookService {
    
    
    @Autowired
    private BookDao bookDao;

    public boolean save(Book book) {
    
    
        bookDao.save(book);
        return true;
    }

    public boolean update(Book book) {
    
    
        bookDao.update(book);
        return true;
    }

    public boolean delete(Integer id) {
    
    
        bookDao.delete(id);
        return true;
    }

    public Book getById(Integer id) {
    
    
        return bookDao.getById(id);
    }

    public List<Book> getAll() {
    
    
        return bookDao.getAll();
    }
}

说明:

​ 通常来说,编写完Service实现类后,就可以对Service层进行方法的测试

步骤五:测试Service层

@RunWith(SpringJUnit4ClassRunner.class) // 注解指定了运行测试的类为SpringJUnit4ClassRunner,这样就能够在测试过程中启动Spring容器。
@ContextConfiguration(classes = SpringConfig.class) //注解指定了使用哪个配置类来加载Spring容器。
public class BookServiceTest {
    
    

    @Autowired
    private BookService bookService;

    @Test
    public void testGetById(){
    
    
        // 测试根据ID获取图书信息的方法
        Book book = bookService.getById(1);
        System.out.println(book);
    }

    @Test
    public void testGetAll(){
    
    
        // 测试获取所有图书信息的方法
        List<Book> all = bookService.getAll();
        System.out.println(all);
    }
}

补充:

  • @RunWith注解用于指定测试运行器(test runner),它告诉JUnit在运行测试时使用特定的运行器来执行测试。
  • @ContextConfiguration注解用于指定Spring容器的配置信息,告诉JUnit在运行测试之前要加载哪些配置。

步骤六:编写表现层

@RestController
@RequestMapping("/books")
public class BookController {
    
    

    @Autowired
    private BookService bookService;

    @PostMapping
    public boolean save(@RequestBody Book book) {
    
    
        return bookService.save(book);
    }

    @PutMapping
    public boolean update(@RequestBody Book book) {
    
    
        return bookService.update(book);
    }

    @DeleteMapping("/{id}")
    public boolean delete(@PathVariable Integer id) {
    
    
        return bookService.delete(id);
    }

    @GetMapping("/{id}")
    public Book getById(@PathVariable Integer id) {
    
    
        return bookService.getById(id);
    }

    @GetMapping
    public List<Book> getAll() {
    
    
        return bookService.getAll();
    }
}

6.4表现层数据封装

笔记小结:

  • 概述:便在控制器(Controller)和视图(View)之间进行数据传递和交互。

  • 步骤一:设置统一数据的返回结果类

    • 前端接收数据格式-创建结果模型类,封装数据到data属性中
    • 前端接收数据格式-封装特殊消息到message(msg)属性中
    • 前端接收数据格式-封装操作结果到code属性中
  • 步骤二:设置统一数据的返回状态码

    //状态码
    public class Code {
           
           
        public static final Integer SAVE_OK = 20011;
        public static final Integer DELETE_OK = 20021;
    }
    
  • 步骤三:优化表现层类

    • 统一每一个控制器方法返回值

​ 在表现层(Presentation Layer),数据封装指的是将请求数据和响应数据进行适当的封装和转换,以便在控制器(Controller)和视图(View)之间进行数据传递和交互。

步骤一:设置统一数据的返回结果类

@Date
public class Result {
    
    
    //描述统一格式中的数据
    private Object data;
    //描述统一格式中的编码,用于区分操作,可以简化配置0或1表示成功失败
    private Integer code;
    //描述统一格式中的消息,可选属性
    private String msg;

    public Result() {
    
    
    }

    public Result(Integer code,Object data) {
    
    
        this.data = data;
        this.code = code;
    }

    public Result(Integer code, Object data, String msg) {
    
    
        this.data = data;
        this.code = code;
        this.msg = msg;
    }
}

说明:

​ Result类中的字段并不是固定的,可以根据需要自行增减提供若干个构造方法,方便操作

  • 前端接收数据格式-创建结果模型类,封装数据到data属性中
  • 前端接收数据格式-封装特殊消息到message(msg)属性中
  • 前端接收数据格式-封装操作结果到code属性中

步骤二:设置统一数据的返回状态码

//状态码
public class Code {
    
    
    public static final Integer SAVE_OK = 20011;
    public static final Integer DELETE_OK = 20021;
    public static final Integer UPDATE_OK = 20031;
    public static final Integer GET_OK = 20041;

    public static final Integer SAVE_ERR = 20010;
    public static final Integer DELETE_ERR = 20020;
    public static final Integer UPDATE_ERR = 20030;
    public static final Integer GET_ERR = 20040;
}

步骤三:优化表现层类

//统一每一个控制器方法返回值
@RestController
@RequestMapping("/books")
public class BookController {
    
    

    @Autowired
    private BookService bookService;

    @PostMapping
    public Result save(@RequestBody Book book) {
    
    
        boolean flag = bookService.save(book);
        return new Result(flag ? Code.SAVE_OK:Code.SAVE_ERR,flag);
    }

    @PutMapping
    public Result update(@RequestBody Book book) {
    
    
        boolean flag = bookService.update(book);
        return new Result(flag ? Code.UPDATE_OK:Code.UPDATE_ERR,flag);
    }

    @DeleteMapping("/{id}")
    public Result delete(@PathVariable Integer id) {
    
    
        boolean flag = bookService.delete(id);
        return new Result(flag ? Code.DELETE_OK:Code.DELETE_ERR,flag);
    }

    @GetMapping("/{id}")
    public Result getById(@PathVariable Integer id) {
    
    
        Book book = bookService.getById(id);
        Integer code = book != null ? Code.GET_OK : Code.GET_ERR;
        String msg = book != null ? "" : "数据查询失败,请重试!";
        return new Result(code,book,msg);
    }

    @GetMapping
    public Result getAll() {
    
    
        List<Book> bookList = bookService.getAll();
        Integer code = bookList != null ? Code.GET_OK : Code.GET_ERR;
        String msg = bookList != null ? "" : "数据查询失败,请重试!";
        return new Result(code,bookList,msg);
    }
}

说明:

​ 通常来说,可以在表现层实现业务逻辑的处理,我们可以在此设置操作结果的返回处理

6.5异常处理器

笔记小结:

  • 概述:异常处理器(Exception Handler)是一种用于捕获和处理应用程序中出现的异常的机制。
  • 整合流程:
    • 步骤一:新建异常处理类
      • @RestControllerAdvice 是一个用于全局异常处理的注解。它可以在整个应用程序范围内捕获和处理异常,并将处理结果以统一的格式返回给客户端
      • @ExceptionHandler(Exception.class) 注解用于标识一个异常处理方法,指定了要捕获和处理的异常类型。在这里,它捕获的是 Exception 类型的异常。
  • 注解解释:
    • @RestControllerAdvice: 是一个用于全局异常处理的注解。为Rest风格开发的控制器类做增强
    • @ExceptionHandler:设置指定异常的处理方案,功能等同于控制器方法,出现异常后终止原始控制器执行,并转入当前方法执行

​ 异常处理器(Exception Handler)是一种用于捕获和处理应用程序中出现的异常的机制。在Java的Web应用中,异常处理器通常用于捕获控制器(Controller)中发生的异常,并根据需要进行适当的处理和响应。

出现异常现象的常见位置与常见诱因如下:

  • 框架内部抛出的异常:因使用不合规导致
  • 数据层抛出的异常:因外部服务器故障导致(例如:服务器访问超时)
  • 业务层抛出的异常:因业务逻辑书写错误导致(例如:遍历业务书写操作,导致索引异常等)
  • 表现层抛出的异常:因数据收集、校验等规则导致(例如:不匹配的数据类型间导致异常)
  • 工具类抛出的异常:因工具类书写不严谨不够健壮导致(例如:必要释放的连接长期未释放等)

步骤一:新建异常处理类

controller包下,新建ProjectExceptionAdvice类

@RestControllerAdvice
public class ProjectExceptionAdvice {
    
    

    /**
     * 全局异常处理方法
     * @param ex 异常对象
     * @return 自定义的结果对象
     */
    @ExceptionHandler(Exception.class)
    public Result doException(Exception ex){
    
    
        return new Result(666,null);
    }
}

说明:

  • @RestControllerAdvice 是一个用于全局异常处理的注解。它可以在整个应用程序范围内捕获和处理异常,并将处理结果以统一的格式返回给客户端
  • @ExceptionHandler(Exception.class) 注解用于标识一个异常处理方法,指定了要捕获和处理的异常类型。在这里,它捕获的是 Exception 类型的异常。

补充:

  • 名称:@RestControllerAdvice

    • 类型:类注解

    • 位置: Rest风格开发的控制器增强类定义上方

    • 作用:为Rest风格开发的控制器类做增强

    • 范例:

      @RestControllerAdvice
      public class ProjectExceptionAdvice {
               
               }
      
    • 说明:

      • 此注解自带@ResponseBody注解与@Component注解,具备对应的功能
  • 名称:@ExceptionHandler

    • 类型:方法注解

    • 位置:专用于异常处理的控制器方法上方

    • 作用:设置指定异常的处理方案,功能等同于控制器方法,出现异常后终止原始控制器执行,并转入当前方法执行

    • 范例:

      @RestControllerAdvice
      public class ProjectExceptionAdvice {
               
               
          @ExceptionHandler(Exception.class)
          public Result doException(Exception ex){
               
               
              return new Result(666,null);
          }
      
    • 此类方法可以根据处理的异常不同,制作多个方法分别处理对应的异常

6.6项目异常处理

笔记小结:

  • 概述:项目异常处理是指在开发过程中对各种异常情况进行捕获、处理和反馈的一种机制
  • 处理方案:
    • 业务异常(BusinessException):用户操作不规范导致
    • 系统异常(SystemException):可预期的代码操作异常
    • 其他异常(Exception):不可预期的异常
  • 操作步骤
    • 步骤一:自定义项目系统级异常
      • 继承RuntimeException运行时异常类
      • 根据业务实现构造方法,例如:SystemException(Integer code, String message) 、SystemException(Integer code, String message, Throwable cause)
    • 步骤二:自定义项目业务级异常
      • 继承RuntimeException运行时异常类
      • 根据业务实现构造方法,例如: BusinessException(Integer code, String message) 、BusinessException(Integer code, String message, Throwable cause)
    • 步骤三:自定义异常编码(持续补充)
    • 步骤四:出发自定义异常
    • 步骤五:拦截并处理异常(重点)
      • 定义项目异常的处理方案,通过@ExceptionHandler此注解,来进行异常的处理

​ 项目异常处理是指在开发过程中对各种异常情况进行捕获、处理和反馈的一种机制。异常处理的目的是保证系统的稳定性和可靠性,以提供良好的用户体验。

项目异常处理方案

  • 业务异常(BusinessException)
    • 发送对应消息传递给用户,提醒规范操作
  • 系统异常(SystemException)
    • 发送固定消息传递给用户,安抚用户
    • 发送特定消息给运维人员,提醒维护
    • 记录日志
  • 其他异常(Exception)
    • 发送固定消息传递给用户,安抚用户
    • 发送特定消息给编程人员,提醒维护(纳入预期范围内)
    • 记录日志

步骤一:自定义项目系统级异常

//自定义异常处理器,用于封装异常信息,对异常进行分类
public class SystemException extends RuntimeException{
    
    
    private Integer code;

    public Integer getCode() {
    
    
        return code;
    }

    public void setCode(Integer code) {
    
    
        this.code = code;
    }

    public SystemException(Integer code, String message) {
    
    
        super(message);
        this.code = code;
    }

    // Throwable为异常报错的对象
    public SystemException(Integer code, String message, Throwable cause) {
    
    
        super(message, cause);
        this.code = code;
    }
}

说明:

  • 系统级别异常,可能为编程人员疏漏而导致BUG所引起的异常
  • 对于成员变量code,此处只需要实现code的Set方法。后续在使用时,只需要获取此对象的Code即可

步骤二:自定义项目业务级异常

//自定义异常处理器,用于封装异常信息,对异常进行分类
public class BusinessException extends RuntimeException{
    
    
    private Integer code;

    public Integer getCode() {
    
    
        return code;
    }

    public void setCode(Integer code) {
    
    
        this.code = code;
    }

    public BusinessException(Integer code, String message) {
    
    
        super(message);
        this.code = code;
    }

    // Throwable为异常报错的对象
    public BusinessException(Integer code, String message, Throwable cause) {
    
    
        super(message, cause);
        this.code = code;
    }
}

说明:

  • 系统级别异常,可能为操作系统问题,或数据库连接不通而导致的异常

步骤三:自定义异常编码(持续补充)

public static final Integer SYSTEM_ERR = 50001;
public static final Integer SYSTEM_TIMEOUT_ERR = 50002;
public static final Integer SYSTEM_UNKNOW_ERR = 59999;

public static final Integer BUSINESS_ERR = 60002;

步骤四:触发自定义异常

image-20230531135942393

说明:

​ 通过模拟方式,来仅以异常的分类与抛出

步骤五:拦截并处理异常

//@RestControllerAdvice用于标识当前类为REST风格对应的异常处理器
@RestControllerAdvice
public class ProjectExceptionAdvice {
    
    
    // 用于处理用户操作不当而导致的异常
    @ExceptionHandler(BusinessException.class)
    public Result doBusinessException(BusinessException ex){
    
    
        return new Result(ex.getCode(),null,ex.getMessage());
    }

    //@ExceptionHandler用于设置当前处理器类对应的异常类型
    @ExceptionHandler(SystemException.class)
    public Result doSystemException(SystemException ex){
    
    
        //记录日志
        //发送消息给运维
        //发送邮件给开发人员,ex对象发送给开发人员
        return new Result(ex.getCode(),null,ex.getMessage());
    }

    //除了自定义的异常处理器,保留对Exception类型的异常处理,用于处理非预期的异常
    @ExceptionHandler(Exception.class)
    public Result doOtherException(Exception ex){
    
    
        //记录日志
        //发送消息给运维
        //发送邮件给开发人员,ex对象发送给开发人员
        return new Result(Code.SYSTEM_UNKNOW_ERR,null,"系统繁忙,请稍后再试!");
    }
}

步骤六:异常处理器效果对比

image-20230531140236636

说明:

​ 可以看出,当资源访问正常和异常的情况,软件系统可以做出准确的提示

7.拦截器

笔记小结:

  1. 概述:
  • 定义:拦截器(Interceptor)是在Web开发中常用的一种技术,用于在请求处理的过程中对请求进行拦截处理

  • 作用:在指定的方法调用前后执行预先设定后的的代码以及阻止原始方法的执行

  • 拦截器和过滤器区别:

    • 归属不同
    • 拦截内容不同
    • ……

    image-20230814122356401

  1. 基本用例:
  • 步骤一:声明拦截器的Bean,并实现HandlerInterceptor接口(注意:扫描加载bean)
  • 步骤二:定义配置类
    • 方式一:定义配置类,继承WebMvcConfigurationSupport,实现addInterceptor方法(注意:扫描加载配置)
    • 方式二:实现标准接口webMvcConfigurer简化开发(注意:侵入式较强)
  • 补充:
    • 配置静态资源映射:addResourceHandlers,addResourceLocations
    • 配置动态资源的拦截器:addInterceptors,addPathPatterns
  1. 拦截器参数:
  • 前置处理:在请求拦截前做的一些处理
  • 后置处理:在请求拦截后做的一些处理
  1. 拦截器工作流程:
  • 拦截器:单个拦截器处理

    image-20230531144547904

  • 拦截链:多个拦截器处理

    image-20230531154459978

7.1概述

7.1.1定义

​ 拦截器(Interceptor)是在Web开发中常用的一种技术,用于在请求处理的过程中对请求进行拦截和处理。拦截器可以在请求进入控制器处理之前或之后执行一些自定义的逻辑,例如日志记录、权限验证、请求参数预处理等。

image-20230603105043433

7.1.2作用

在指定的方法调用前后执行预先设定后的的代码以及阻止原始方法的执行。作用功能如下:

  1. 权限验证:拦截器可以用于验证用户的权限,例如检查用户是否已登录、是否具有执行某个操作的权限等。如果权限验证失败,可以中断请求或进行相应的处理。
  2. 日志记录:拦截器可以用于记录请求的日志信息,包括请求的URL、请求参数、请求时间等,方便后续的统计分析、错误排查等工作。
  3. 请求参数预处理:拦截器可以在请求进入控制器之前对请求参数进行预处理,例如对参数进行校验、格式化、加密等操作,以保证参数的正确性和安全性。
  4. 异常处理:拦截器可以用于捕获控制器中抛出的异常,并进行统一的异常处理,例如返回统一的错误信息页面、记录异常日志等。
  5. 请求响应处理:拦截器可以在请求处理完成后对响应进行处理,例如设置响应头信息、修改响应内容等。
  6. 缓存控制:拦截器可以用于控制响应的缓存策略,例如设置响应的缓存时间、验证缓存的有效性等。

通常情况下,在实际开发中,拦截器通常用于Token权限

7.1.3拦截器和过滤器区别

拦截器(Interceptor)和过滤器(Filter)都是在Web开发中用于对请求进行处理的组件,但它们有以下区别:

  1. 应用范围不同:拦截器是针对Spring MVC框架而言的,它是基于方法的拦截,只能拦截到控制器中的请求处理方法。而过滤器是Servlet规范定义的,它是基于URL模式的拦截,可以拦截到所有请求,包括静态资源和Servlet请求。

  2. 执行顺序不同:拦截器是在处理器(Controller)之前或之后执行的,可以对请求进行预处理或后处理。过滤器是在请求进入Servlet容器之前或之后执行的,可以对请求进行过滤、修改或包装。

  3. 功能不同:拦截器主要用于处理请求的业务逻辑,如权限验证、日志记录、异常处理等。过滤器主要用于对请求进行过滤和修改,如字符编码转换、请求包装、请求参数处理等。

  4. 配置方式不同:拦截器的配置是在Spring MVC的配置文件中进行的,需要手动配置拦截器的顺序和路径匹配规则。过滤器的配置是在web.xml文件中进行的,通过配置URL模式来指定过滤器的拦截路径和顺序。

  5. 归属不同:Filter属于Servlet技术Interceptor属于SpringMVC技术

  6. 拦截内容不同:Filter对所有访问进行增强,Interceptor仅针对springMVC的访问进行增强

image-20230603105732011

​ 总体来说,拦截器更加灵活和精细化,适用于处理业务逻辑;过滤器更加通用,适用于对请求进行过滤和修改。在使用时,可以根据具体需求选择拦截器或过滤器来实现相应的功能。

7.2基本用例-拦截器实现

步骤一:定义拦截器

  • 声明拦截器的Bean,并实现HandlerInterceptor接口

注意:

扫描加载bean,记得添加@Component注解

@Component //拦截器
//定义拦截器类,实现HandlerInterceptor接口
//注意当前类必须受Spring容器控制
public class ProjectInterceptor implements HandlerInterceptor {
    
    
    @Override
    //原始方法调用前执行的内容
    //返回值类型可以拦截控制的执行,true放行,false终止
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
    
        System.out.println("preHandle..."+contentType);
        return true;
    }

    @Override
    //原始方法调用后执行的内容
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    
    
        System.out.println("postHandle...");
    }

    @Override
    //原始方法调用完成后执行的内容
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    
    
        System.out.println("afterCompletion...");
    }
}

说明:

​ 通常来说,拦截器用于表现层的处理,因此将拦截器放置在Controller包下

步骤二:定义配置类

方式一:定义配置类,继承WebMvcConfigurationSupport,实现addInterceptor方法(注意:扫描加载配置)

@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
    
    
    @Autowired
    private ProjectInterceptor projectInterceptor;

    /**
     * 配置静态资源映射
     */
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
    
    
        registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
    }

    /**
     * 配置动态资源的拦截器
     */
    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
    
    
        registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*");
    }
}

说明:

  • 通过以上配置,实现了对静态资源的访问和对指定路径的拦截器配置。
  • 添加拦截器并设定拦截的访问路径,路径可以通过可变参数设置多个

方式二:实现标准接口webMvcConfigurer简化开发(注意:侵入式较强)

@Configuration
@ComponentScan({
    
    "com.itheima.controller"})
@EnableWebMvc
//实现WebMvcConfigurer接口可以简化开发,但具有一定的侵入性
public class SpringMvcConfig implements WebMvcConfigurer {
    
    
    @Autowired
    private ProjectInterceptor projectInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    
    
        //配置多拦截器
        registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*");
    }
}

说明:

通过实现 WebMvcConfigurer接口,也可以达到继承WebMvcConfigurationSupport配置类实现资源拦截的效果

注意:

​ 通过实现WebMvcConfigurer接口来实现拦截器的方式,入侵性较强

7.3拦截器参数

7.3.1前置处理

前置处理就是,在请求拦截前做的一些处理

@Component
//定义拦截器类,实现HandlerInterceptor接口
//注意当前类必须受Spring容器控制
public class ProjectInterceptor implements HandlerInterceptor {
    
    
    @Override
    //原始方法调用前执行的内容
    //返回值类型可以拦截控制的执行,true放行,false终止
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
    
        String contentType = request.getHeader("Content-Type");
        HandlerMethod hm = (HandlerMethod)handler;
        System.out.println("preHandle..."+contentType);
        return true;
    }
}

说明:

​ request中可以获得请求头中的信息,此时可以取出便于操作

补充:

  • 参数:
    • request:请求对象
    • response:响应对象
    • handler:被调用的处理器对象,本质上是一个方法对象,对反射技术中的Method对象进行了再包装
  • 返回值
    • 返回值为false,被拦截的处理器将不执行

7.3.2后置处理

public void postHandle(HttpServletRequest request,
                       HttpservletResponse response,
                       object handler,
                       ModelAndview modelAndView) throws Exception {
    
    
    System.out.println( "postHandle..." );
}

补充:

modelAndView:如果处理器执行完成具有返回结果,可以读取到对应数据与页面信息,并进行调整

7.4拦截器工作流程(重点)

7.4.1基本流程

image-20230531144547904

说明:

  • 当没有拦截器的时候,请求通过表现层不会做任何拦截
  • 当有拦截器时,请求通过表现成会对,原始方法调用前执行的内容、原始方法调用后执行的内容、原始方法调用完成后执行的内容进行拦截处理
  • 如果拦截处理后响应了True则表示放行,响应为False表示拦截该请求内容

7.4.2拦截链

image-20230531154459978

说明:

  • 若在第三个拦截器返回False时,则只会运行第一个和第二个拦截的后置处理器
  • 若在第二个拦截器返回False时,则只会运行第一个拦截的后置处理器
  • 若在第一个拦截器返回False时,则一个拦截的后置处理器都不会运行

8.执行过程

笔记小结:

​ 此节比较复杂,我们可以参考外部笔记进行学习:springmvc笔记(尚硅谷)_不会打字314的博客-CSDN博客

8.1SpringMVC常用组件

  • DispatcherServlet:DispatcherServlet是Spring MVC框架的前端控制器。它负责接收所有的HTTP请求,并将请求分发给对应的处理器进行处理。DispatcherServlet是Spring MVC框架的核心组件,它调度请求处理流程中的各个环节。
  • HandlerMapping:HandlerMapping是处理器映射器的一种实现,它根据请求的URL路径映射到具体的处理器(Controller),确定请求对应的处理器。HandlerMapping可以根据不同的映射策略进行配置,例如基于URL路径、基于注解等。
  • Handler:Handler是处理器的意思,也称为Controller。它是业务逻辑的具体执行者,负责处理请求并返回处理结果。在Spring MVC中,Handler可以是一个普通的POJO类,通过方法来处理请求。
  • HandlerAdapter:HandlerAdapter是处理器适配器,用于执行处理器的方法,并将请求的参数进行适配,使其能够正确处理请求。HandlerAdapter根据处理器的类型和处理方法的参数类型,选择合适的适配器来执行处理器方法。
  • ViewResolver:ViewResolver是视图解析器,用于将处理结果渲染为最终的视图。它根据处理器返回的逻辑视图名解析出具体的视图对象,例如ThymeleafView、InternalResourceView、RedirectView等。ViewResolver负责将模型数据传递给视图,最终呈现给用户。
  • View:View是视图的意思,它负责展示最终的处理结果给用户。视图可以是HTML页面、JSON数据、XML文档等。在Spring MVC中,视图通常通过模板引擎进行渲染,将模型数据填充到模板中生成最终的视图。

image-20230602102312053

​ 这些组件共同协作,完成请求的处理和响应的生成。DispatcherServlet作为前端控制器接收请求,HandlerMapping确定请求对应的处理器,HandlerAdapter执行处理器方法,ViewResolver解析视图,View负责最终的呈现。这个处理流程是Spring MVC框架中的核心机制,通过配置和组合这些组件,可以灵活地实现不同的请求处理和视图渲染逻辑。

8.2DispatcherServlet初始化过程

​ DispatcherServlet 本质上是一个 Servlet,所以天然的遵循 Servlet 的生命周期。所以宏观上是 Servlet 生命周期来进行调度

image-20230602094211246

说明:

1.GenericServlet类会实现Service接口并重写Init方法

image-20230602112204665

说明:

  • 因为Servler为接口,因此GenericServlet类会实现Service接口并重写Init方法
  • 因为HttpServlet类跟GenericSerlver为继承的关系,因此GenericServlet类中的两个init方法,会保留在HttpServlet类中

2.HttpServlet类没有重写GenericServlet类中的init方法

image-20230602112719365

说明:

​ HttpServlet类虽然继承了GenericServlet类,但并没有重写GenericServlet类中的init方法

3.HttpServletBean类重写了GenericServlet类中的init方法,并调用了initServletBean方法

image-20230602112804189

说明:

​ HttpServletBean类重写了GenericServlet类中的init方法

image-20230602113308369

说明:

​ HttpServletBean类在init方法中,调用了一个重要的方法initServletBean方法,来初始化ServletBean

4.FrameworkServler类重写了HttpServletBean类中的initServletBean方法

image-20230602113741285

说明:

​ FrameworkServler类在initServletBean方法中调用了SpringMVC的IOC容器初始化的方法this.initWebApplicationContext();

5.在FrameworkServler类中,会调用initWebApplicationContext方法

image-20230602114621108

说明:

​ 因为此方法在首次执行时,创建的WebApplicationContext为空,因此会调用this.createWebApplicationContext(rootContext);这个方法来进行WebApplicationContext的创建

5.1.在FrameworkServler类中,调用createWebApplicationContext方法来创建WebApplicationContext

image-20230602114841908

image-20230602191454335

说明:

​ 在FrameworkServler类中调用wac.setParent(parent);方法,设置Spring IOC容器为Spring MVC的父容器

5.2在FrameworkServler类中,调用this.onRefresh(wac)和this.getServletContext().setAttribute(attrName, wac);方法

image-20230602192539137

说明:

  • 调用this.onRefresh(wac)方法,会执行子类DispatcherServlet类中的OnRefresh方法来初始化策略

    image-20230602193255953

  • 调用this.getServletContext().setAttribute(attrName, wac);方法,将webApplicationContext对象在应用域中进行共享

6.在DispatcherServler类中,会重写FrameworkServler类中的onRefresh方法

image-20230602193437015

image-20230602193510752

说明:

​ 里面初始化的各种容器,其实就是在DispatcherServlet初始化时进行加载的

8.3DispatcherServlet服务过程

1.GenericServlet类会实现Service接口继承此Service方法

image-20230602194934369

说明:

​ 在GenericServlet类中,并没有重写Service接口中的service方法,而是直接继承

2.在HttpServlet类中,重写了GenericServlet类中是Service方法

image-20230602195300896

说明:

​ 在HttpServlet类的Service方法中将ServletRequest和ServletResponse对象转换为了子类HttpServletRequest和HttpServletRequest,HttpServletResponse对象

3.在HttpServlet类中的service方法中,对请求进行分发

image-20230602195750111

说明:

​ 不同的方法执行对应不同的函数

4.在HttpServletBean中,并没有重写HttpServlet类中的service方法、以及do开头的方法

image-20230602195928441

说明:

​ 在HttpServlet类中,并没有重写HttpServlet类中的service方法、以及do开头的方法

5.在FramesServlet类中,无论如何都会执行此类的this.processRequest方法

image-20230602200359274

说明:

​ super.service(request,response)方法,会执行HttpServlet中的service方法,而不是HttpServiceBean类中的,因为HttpServiceBean类中,并没有实现任何serivice方法

image-20230602200529945

说明:

​ 在FramesServlet类中的processRequest方法中实现了一个重要的方法doService

image-20230602200606022

说明:

​ 在FramesServlet类中的doService方法中,发现doService是一个抽象类方法。因此,我们需要看FramesServlet类的子类中,如何实现此方法

6.在DispatcherServlet类中,最终会执行this.doDispatch(request, response);方法,来进行服务的处理

image-20230602201202436

8.4DisPatcheServlet调用组件处理请求的过程

1.在DispatcherServlet类中通过mappedHandler = this.getHandler(processedRequest);来为处理执行链赋值

image-20230603100700034

说明:

  • 第472行HandlerExecutionChain mappedHandler = null;中,mappedHandler叫执行链,包含控制器方法(处理器)、拦截器集合、拦截器索引

    image-20230603101356041

image-20230603101506491

说明:

  • HandlerAdapter用于调用控制器的方法

    image-20230603101557702

image-20230603101946004

说明:

​ ha.handle方法是适配器中对请求方法的参数类型、请求头、请求cookie进行处理

image-20230603102443189

说明:

​ 可以发现,在DispatcherServler类中的doDispatch方法中,并没有体现来渲染视图的方法。其实this.processDispatchResult方法,是用于对后续步骤进行处理的方法,在此方法中会对视图进行后续的处理

8.5执行流程(了解)

待补充……

知识加油站(重点)

1.@RequestBody与@RequestParam区别

  • 区别
    • @RequestParam用于接收url地址传参,表单传参【application/x-www-form-urlencoded】
    • @RequestBody用于接收json数据【application/json】
  • 应用
    • 后期开发中,发送json格式数据为主,@RequestBody应用较广
    • 如果发送非json格式数据,选用@RequestParam接收请求参数

2.Idea中,打开类关系继承图

快捷键:Ctrl+H

image-20230530141253472

3.@RequestBody @RequestParam @PathVariable区别

  • 区别
    • @RequestParam用于接收url地址传参或表单传参
    • @RequestBody用于接收json数据
    • @PathVariable用于接收路径参数,使用{参数名称}描述路径参数
  • 应用
    • 后期开发中,发送请求参数超过1个时,以json格式为主,@RequestBody应用较广
    • 如果发送非json格式数据,选用@RequestParam接收请求参数
    • 采用RESTful进行开发,当参数数量较少时,例如1个,可以采用@PathVariable接收请求路径变量,通常用于传递id值

4.@RestControllerAdvice 和 @ControllerAdvice 区别

@RestControllerAdvice@ControllerAdvice 是Spring MVC中用于全局异常处理和全局数据绑定的注解,它们的区别如下:

  1. @RestControllerAdvice@ControllerAdvice@ResponseBody 的组合注解,表示一个控制器增强器,用于全局异常处理和全局数据绑定,并将结果以JSON格式返回给客户端。
  2. @ControllerAdvice 是一个控制器增强器,用于全局异常处理和全局数据绑定,它可以应用于所有使用 @Controller@RestController 注解的控制器类。
  3. 使用 @RestControllerAdvice 注解的类,可以在方法上使用 @ExceptionHandler@InitBinder@ModelAttribute 注解,实现全局的异常处理、数据绑定和预处理。
  4. 使用 @ControllerAdvice 注解的类,可以在方法上使用 @ExceptionHandler@InitBinder@ModelAttribute 注解,实现全局的异常处理、数据绑定和预处理。但是返回的结果需要通过 @ResponseBody 注解或其他方式转换为JSON格式。

综上所述,主要区别在于返回结果的格式。@RestControllerAdvice 注解的类返回的结果会直接以JSON格式返回给客户端,而 @ControllerAdvice 注解的类需要通过 @ResponseBody 注解或其他方式将结果转换为JSON格式后返回给客户端。

5.SpringMVC支持ant风格的路径

?:表示任意的单个字符

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

**:表示任意的一层或多层目录

注意:在使用时,只能使用//xxx的方式

6.Model、Map、ModelMap、ModelAndView间的关系

image-20230601084245953

7.AbstractAnnotationConfigDispatcherServletInitializer 和 AbstractDispatcherServletInitializer 类区别

AbstractAnnotationConfigDispatcherServletInitializerAbstractDispatcherServletInitializer是Spring MVC中用于配置和初始化DispatcherServlet的抽象类。

  1. AbstractAnnotationConfigDispatcherServletInitializer:
    • AbstractAnnotationConfigDispatcherServletInitializer是一个抽象类,继承自AbstractDispatcherServletInitializer
    • 该类用于基于Java配置的方式配置和初始化DispatcherServlet。
    • 通过继承该类并实现其中的抽象方法,可以配置RootConfig和ServletConfig的相关类,以及Servlet映射路径等。
    • 它使用AnnotationConfigWebApplicationContext来加载配置类,并将其与DispatcherServlet关联。
  2. AbstractDispatcherServletInitializer:
    • AbstractDispatcherServletInitializer是一个抽象类,是WebApplicationInitializer接口的实现类。
    • 该类用于基于传统的XML配置的方式配置和初始化DispatcherServlet。
    • 它可以用于创建和注册DispatcherServlet,并配置Servlet映射路径等。
    • 通过继承该类并实现其中的抽象方法,可以配置RootConfig和ServletConfig的相关XML配置文件路径。

总结: AbstractAnnotationConfigDispatcherServletInitializer是基于Java配置的方式配置和初始化DispatcherServlet的抽象类,而AbstractDispatcherServletInitializer是基于传统的XML配置的方式配置和初始化DispatcherServlet的抽象类。它们提供了不同的方式来配置和初始化DispatcherServlet,使得开发者可以根据自己的喜好和项目需求选择适合的方式。

8.@RequestHeader注解(重点)

@RequestHeader注解用于将请求头中的值绑定到方法参数上。它可以用于获取特定请求头的值,或者获取所有请求头的值。该注解可以应用于方法参数、方法、类级别的处理器方法上。

  • 获取特定请求头的值
@GetMapping("/example")
public void exampleMethod(@RequestHeader("User-Agent") String userAgent) {
    
    
    // 处理方法逻辑
}
  • 获取所有请求头的值
@GetMapping("/init2")
public void init2(@RequestHeader Map<String, String> headerMap) {
    
    
    
    // 使用Map接收所有的请求头
    System.out.println(headerMap);
    // js中使用header名为addressList,使用map接收后需要使用addresslist
    System.out.println(headerMap.get("addresslist"));  
    
}

9.@CookieValue注解(重点)

@CookieValue注解用于将Cookie中的值绑定到方法参数上。它可以用于获取特定Cookie的值,或者获取所有Cookie的值。该注解可以应用于方法参数、方法、类级别的处理器方法上。

  • 获取特定请求头的值
@GetMapping("/example")
public void exampleMethod(@CookieValue("sessionId") String sessionId) {
    
    
    // 处理方法逻辑
}
  • 获取所有请求头的值
@GetMapping("/example")
public void exampleMethod(@CookieValue Map<String, String> cookies) {
    
    
    // 处理方法逻辑
    for (Map.Entry<String, String> entry : cookies.entrySet()) {
    
    
        String cookieName = entry.getKey();
        String cookieValue = entry.getValue();
        // 处理每个Cookie的逻辑
    }
}

10.HttpServlet容器响应Web客户请求流程

1)Web客户向Servlet容器发出Http请求;

2)Servlet容器解析Web客户的Http请求;

3)Servlet容器创建一个HttpRequest对象,在这个对象中封装Http请求信息;

4)Servlet容器创建一个HttpResponse对象;

​ 5)Servlet容器调用HttpServlet的service方法,这个方法中会根据request的Method来判断具体是执行doGet还是doPost,把HttpRequest和HttpResponse对象作为service方法的参数传给HttpServlet对象;

6)HttpServlet调用HttpRequest的有关方法,获取HTTP请求信息;

7)HttpServlet调用HttpResponse的有关方法,生成响应数据;

8)Servlet容器把HttpServlet的响应结果传给Web客户。

猜你喜欢

转载自blog.csdn.net/D_boj/article/details/132287050