03—Spring MVC

概述

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

入门案例

创建一个Maven项目,在pom.xml中导入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.spark</groupId>
    <artifactId>springmvc-start</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.10.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
</project>

编写表现层Controller

package com.spark.controller;

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

/**
 * UserController class
 * description: UserController
 *
 * @author Administrator
 * @date 2023/7/26
 */
@Controller
public class UserController {
    
    // 提供方法的请求路径
    @RequestMapping("/save")
    @ResponseBody
    public String saveUser(){
        System.out.println("user save ...");
        return "{module:save}";
    }
}

编写SpringMVC配置类加载Controller层注册的Bean

package com.spark.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

/**
 * SpringmvcConfig class
 * description: SpringMvcConfig
 *
 * @author Administrator
 * @date 2023/7/26
 */
@Configuration
@ComponentScan("com.spark.controller")
public class SpringMvcConfig {

}

编写Servlet容器配置类加载SpringMVC配置类

package com.spark.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;

/**
 * ServletInitConfig class
 * description: ServletInitConfig
 *
 * @author Administrator
 * @date 2023/7/26
 */
@Configuration
public class ServletInitConfig extends AbstractDispatcherServletInitializer {

    @Override
    protected WebApplicationContext createServletApplicationContext() {
        AnnotationConfigWebApplicationContext atx = new AnnotationConfigWebApplicationContext();
        atx.register(SpringMvcConfig.class);
        return atx;
    }

    @Override
    protected String[] getServletMappings() {
        // 所有请求
        return new String[] {"/"};
    }

    @Override
    protected WebApplicationContext createRootApplicationContext() {
        return null;
    }
}

配置Tomcat

打开浏览器访问 URL + Controller定义的方法请求路径查看结果

http://localhost:8082/springmvc_start/save

入门案例的工作流程

① 服务器启动初始化流程

  1. 服务器启动,执行ServletInitConfig类初始化web容器
  2. 执行createServletApplicationContext方法,创建了WebApplicationContext对象
  3. 加载SpringMVC配置类
  4. 执行@ComponentScan加载对应bean
  5. 加载UserController,每个@RequestMapping的名称对应一个具体的方法
  6. 执行getServletMappings方法,定义所有请求都通过SpringMVC 

② 发送save请求过程

  1. 发送请求
  2. Web容器发现所有请求都通过SpringMVC,将请求交由SpringMVC处理
  3. 解析请求路径/save
  4. 匹配到对应的方法save()
  5. 执行save()方法
  6. 检测到@ResponseBody直接将save()返回值作为响应体返回给请求方 

Bean加载过程 

一个完整的SpringMVC请求涉及到有表现层(Controller)、数据层(Dao)、业务层(Service)

Controller层由SpringMVC加载,业务层和数据层由Spring加载

Web容器启动后,Spring会加载到SpringMVC的bean,如何进行避免? 

加载Spring控制的bean时排除掉SpringMVC控制的bean

方式一:Spring加载的bean设定扫描范围,并排除掉Controller包内的bean

package com.spark.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;

/**
 * SpringConfig class
 * description: SpringConfig
 *
 * @author Administrator
 * @date 2023/7/26
 */
@Configuration
// 方式一:排除掉Controller层,排除规则为注解方式
@ComponentScan(value = "com.spark",
        excludeFilters = @ComponentScan.Filter(
                type = FilterType.ANNOTATION
        )
)
public class SpringConfig {
}

方式二:Spring加载的bean设定扫描范围为精确范围

package com.spark.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;

/**
 * SpringConfig class
 * description: SpringConfig
 *
 * @author Administrator
 * @date 2023/7/26
 */
@Configuration
// 方式二:设定精确的扫描范围
@ComponentScan({"com.spark.dao","com.spark.service"})
public class SpringConfig {
}

在Servlet容器配置类中注册Spring配置类

package com.spark.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;

/**
 * ServletInitConfig class
 * description: ServletInitConfig
 *
 * @author Administrator
 * @date 2023/7/26
 */
@Configuration
public class ServletInitConfig extends AbstractDispatcherServletInitializer {

    @Override
    protected WebApplicationContext createServletApplicationContext() {
        AnnotationConfigWebApplicationContext atx = new AnnotationConfigWebApplicationContext();
        atx.register(SpringMvcConfig.class);
        return atx;
    }

    @Override
    protected String[] getServletMappings() {
        // 所有请求
        return new String[] {"/"};
    }

    @Override
    protected WebApplicationContext createRootApplicationContext() {
        // 注册Spring配置类
        AnnotationConfigWebApplicationContext atx = new AnnotationConfigWebApplicationContext();
        atx.register(SpringConfig.class);
        return atx;
    }
}

拓展:简化开发Servlet容器配置类

package com.spark.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

/**
 * ServletInitConfig class
 * description: ServletInitConfig
 *
 * @author Administrator
 * @date 2023/7/26
 */
@Configuration
public class ServletInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer{

    @Override
    protected Class<?>[] getRootConfigClasses() {
        // 注册Spring配置类
        return new Class[]{SpringConfig.class};
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        // 注册SpringMVC配置类
        return new Class[]{SpringMvcConfig.class};
    }

    @Override
    protected String[] getServletMappings() {
        // 所有请求
        return new String[]{"/"};
    }
}

Postman 

Postman是一款功能强大的网页调试与发送网页Http请求的Chrome插件,常用于进行接口测试

下载地址:https://www.postman.com/downloads/ 

安装完成后,打开让选择注册用户,可以不进行注册,关闭重新打开就可以进入

启动入门案例IDEA中配置的Tomcat,输入Controller层方法配置的接口地址,查看响应结果

请求与响应 

设置请求路径 

团队多人开发,每个人设置不同的请求路径,有可能会出现冲突,当多人的请求路径相同时,项目启动会出现错误。

通常会在请求路径前设置模块名作为请求路径前缀来解决问题

设置请求路径使用@RequestMapping注解,该注解可以用在类或方法上,前缀一般设置在Controller类上

package com.spark.controller;

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

/**
 * UserController class
 * description: UserController
 *
 * @author Administrator
 * @date 2023/7/26
 */
@Controller
// 设置前缀,一般使用模块名
@RequestMapping("/user")
public class UserController {

    // 提供方法的请求路径
    @RequestMapping("/save")
    @ResponseBody
    public String saveUser(){
        System.out.println("user save ...");
        return "{module:save}";
    }
}

请求方式GET和POST 

创建一个新的Maven项目,在Controller层添加一个方法用于展示请求传递的参数

package com.spark.controller;

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

/**
 * UserController class
 * description: UserController
 *
 * @author Administrator
 * @date 2023/7/26
 */
@Controller
// 设置前缀,一般使用模块名
@RequestMapping("/user")
public class UserController {
    
    // 接收请求参数
    @RequestMapping("/commonParams")
    @ResponseBody
    public String commonParams(String name,int age){
        System.out.println("普通参数name===>"+name);
        System.out.println("普通参数age===>"+age);
        return "{module:commonParams}";
    }
}

① GET请求传参

普通参数:URL地址传参,地址参数名与形参变量名相同,定义形参即可接收参数

② POST请求传参

普通参数:使用form表单进行传参,表单参数名与形参变量名相同,定义形参即可接收参数

传递中文会出现乱码问题,需要在Servlet容器配置类中添加过滤器配置,处理中文乱码

package com.spark.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

import javax.servlet.Filter;

/**
 * ServletInitConfig class
 * description: ServletInitConfig
 *
 * @author Administrator
 * @date 2023/7/26
 */
@Configuration
public class ServletInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer{

    @Override
    protected Class<?>[] getRootConfigClasses() {
        // 注册Spring配置类
        return new Class[]{SpringConfig.class};
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        // 注册SpringMVC配置类
        return new Class[]{SpringMvcConfig.class};
    }

    @Override
    protected String[] getServletMappings() {
        // 所有请求
        return new String[]{"/"};
    }
	
    // 添加过滤器配置
    @Override
    protected Filter[] getServletFilters() {
        CharacterEncodingFilter filter = new CharacterEncodingFilter();
        // 设置字符编码
        filter.setEncoding("UTF-8");
        return new Filter[]{filter};
    }
}

五种请求参数  

① 普通参数传递

普通参数传递要求形参与传参的参数名一致,如果不一致,需要使用@RequestParam注解指定传参的参数名

举例:形参列表的参数名为userName,传参的参数名为name

// 接收请求参数
@RequestMapping("/commonParams")
@ResponseBody
// 将传参的name属性值赋值给形参的userName
public String commonParams(@RequestParam("name") String userName){
    System.out.println("普通参数userName===>"+userName);
    return "{module:commonParams}";
}

② POJO类型参数传递

POJO参数传递要求请求参数名与形参对象属性名相同,定义POJO类型参数即可接收参数,将请求参数封装为实体类

package com.spark.entity;

/**
 * UserEntity class
 * description: UserEntity
 *
 * @author Administrator
 * @date 2023/7/30
 */
public class UserEntity {
    
    private String userName;
    
    private int age;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public int getAge() {
        return age;
    }

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

    @Override
    public String toString() {
        return "UserEntity{" +
                "userName='" + userName + '\'' +
                ", age=" + age +
                '}';
    }
}

Controller层定义方法

// 接收POJO参数
@RequestMapping("/pojoParams")
@ResponseBody
public String pojoParams(UserEntity user){
    System.out.println("user:::"+user);
    return "{module:pojoParams}";
}

③ 嵌套POJO类型参数传递

当实体类中包含其他实体类情况下,请求参数名与形参对象属性名相同,按照对象层次结构关系即可接收嵌套POJO属性参数

添加Address实体类

package com.spark.entity;

/**
 * AddressEntity class
 * description: AddressEntity
 *
 * @author Administrator
 * @date 2023/7/30
 */
public class AddressEntity {
    
    private String city;

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    @Override
    public String toString() {
        return "AddressEntity{" +
                "city='" + city + '\'' +
                '}';
    }
}

UserEntity实体类中添加AddressEntity实体类

package com.spark.entity;

/**
 * UserEntity class
 * description: UserEntity
 *
 * @author Administrator
 * @date 2023/7/30
 */
public class UserEntity {

    private String userName;

    private int age;

    private AddressEntity address;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public int getAge() {
        return age;
    }

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

    public AddressEntity getAddress() {
        return address;
    }

    public void setAddress(AddressEntity address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "UserEntity{" +
                "userName='" + userName + '\'' +
                ", age=" + age +
                ", address=" + address +
                '}';
    }
}

Controller层定义方法

// 接收嵌套POJO参数
@RequestMapping("/pojoNestParams")
@ResponseBody
public String pojoNestParams(UserEntity user){
    System.out.println("user:::"+user);
    return "{module:pojoParams}";
}

④ 数组类型参数传递

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

Controller层定义方法

// 接收数组类型参数
@RequestMapping("/arrayParams")
@ResponseBody
public String arrayParams(String [] likes){
    System.out.println(Arrays.toString(likes));
    return "{module:arrayParams}";
}

⑤ 集合参数传递

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

Controller层定义方法

// 接收集合类型参数
@RequestMapping("/listParams")
@ResponseBody
public String listParams(@RequestParam List<String>likes){
    System.out.println(likes);
    return "{module:listParams}";
}

集合类型参数加@RequestParam原因是因为系统会默认将List看做为引用数据类型,会去调用其构造方法去初始化,List类型为接口没有构造方法

json数据传递 

在项目中数据传递一般是使用的是json对象和json数组。

① 实现json对象传递

pom.xml中引入依赖

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

在SpringMVC配置类中开启json转换为实体

package com.spark.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

/**
 * SpringmvcConfig class
 * description: SpringMvcConfig
 *
 * @author Administrator
 * @date 2023/7/26
 */
@Configuration
@ComponentScan("com.spark.controller")
@EnableWebMvc
public class SpringMvcConfig {

}

Controller层定义方法

// 接收json类型参数
@RequestMapping("/jsonParams")
@ResponseBody
public String jsonParams(@RequestBody UserEntity user){
    System.out.println(user);
    return "{module:jsonParams}";
}

@RequestBody将请求中请求体包含的数据传递给请求参数,此注解一个处理器方法只能用一次

Postman发送请求

② json数组参数传递 

Controller层定义方法

// 接收json类型参数
@RequestMapping("/jsonArrayParams")
@ResponseBody
public String jsonArrayParams(@RequestBody List<UserEntity> users){
    System.out.println(users);
    return "{module:jsonArrayParams}";
}

Postman发送请求

@RequestParam和@RequestBody

区别

  • @RequestParam用于接收url地址传参,表单传参
  • @RequestBody用于接收json数据  

应用

  • 后期开发中,发送json格式数据为主,@RequestBody应用较广
  • 如果发送非json数据,选用@RequestParam接收请求参数 

日期型参数传递 

日期类型基于系统不同格式也不尽相同

接收形参时,根据不同的日期格式设置不同的接收方式

// 接收日期类型参数
@RequestMapping("/dateParams")
@ResponseBody
public String dateParams(Date date, 
                         @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date date2){
    System.out.println("Date:"+date);
    System.out.println("Date2:"+date2);
    return "{module:dateParams}";
}

@DateTimeFormat设置日期类型数据格式 

Rest风格 

概述 

REST表现形式状态转换

传统风格资源描述形式

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

REST风格描述形式

  • http://localhost/user/1
  • http://localhost/user 

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

按照REST风格访问资源时使用行为动作区分对资源进行了何种操作,常用的有四种

  • GET(查询)
  • POST(新增/保存)
  • PUT(修改/更新)
  • DELETE(删除) 

根据REST风格对资源进行访问称为RestFul

入门案例 

使用REST风格约定请求动作,编写表现层Controller

package com.spark.controller;

import com.spark.entity.UserEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * RestController class
 * description: RestController
 *
 * @author Administrator
 * @date 2023/7/31
 */
@Controller
@RequestMapping("/users")
public class RestController {
    
    // 约定save方法的请求动作为POST
    @RequestMapping(value = "/save",method = RequestMethod.POST)
    @ResponseBody
    public String save(@RequestBody UserEntity user){
        System.out.println(user);
        return "{module:save}";
    }
    // 约定delete方法的请求动作为DELETE
    @RequestMapping(value = "/delete",method = RequestMethod.DELETE)
    @ResponseBody
    public String delete(Integer id){
        System.out.println("user id===>"+id);
        return "{module:delete}";
    }
}

当规定了行为动作后,使用其他方式访问接口会报请求方式不支持异常 

地址传参,使用DELETE方式访问却报404

使用路径传参时,方法的形参列表需要使用@PathVariable表明值从url获取

改写delete方法

// 约定delete方法的请求动作为DELETE
// url指定参数名
@RequestMapping(value = "/delete/{id}",method = RequestMethod.DELETE)
@ResponseBody
// 使用@PathVariable注解表明参数从url获取
public String delete(@PathVariable Integer id){
    System.out.println("user id===>"+id);
    return "{module:delete}";
}

简化开发 

@RestController注解是@Controller和@ResponseBody的复合注解,以后在表现层只需要使用@RestController注解就可以管理该类并且方法结果都可以原样返回

@RequestMapping中的method属性指明行为动作,也可以使用具体的注解进行简化开发

  • @GetMapping
  • @PostMapping
  • @PutMapping
  • @DeleteMapping 
package com.spark.controller;

import org.springframework.web.bind.annotation.*;

/**
 * RestController class
 * description: RestController
 *
 * @author Administrator
 * @date 2023/7/31
 */
@RestController
@RequestMapping("/books")
public class RestfulController {
    
    @GetMapping("/allBooks")
    public String getAllBooks() {
        return "{module:getAllBooks}";
    }
    @PostMapping("/saveBooks")
    public String saveBooks(){
        return "{module:saveBooks}";
    }
    @PutMapping("/updateBook")
    public String updateBook(){
        return "{module:updateBook}";
    }
    @DeleteMapping("/deleteBook")
    public String deleteBook(){
        return "{module:deleteBook}";
    }
}

SSM整合 

整合配置 

使用web模版创建一个Maven项目,在pom.xml中引入依赖

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</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.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.2.10.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>2.0.6</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.6</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.2.8</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.49</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
        <version>2.13.1</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.13.1</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-annotations</artifactId>
      <version>2.13.1</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
</dependencies>

resources目录下新建jdbc.properties文件,写入MySQL连接信息

jdbc.driver = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/ssm_db
jdbc.username = root
jdbc.password = root

编写JDBC配置类

package com.spark.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;

import javax.sql.DataSource;

/**
 * JdbcConfig class
 * description: JdbcConfig
 *
 * @author Administrator
 * @date 2023/8/3
 */
public class JdbcConfig {

    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;

    // 注册数据源
    @Bean
    public DataSource getDataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return dataSource;
    }
}

编写Mybatis配置类

package com.spark.config;

import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.Bean;

import javax.sql.DataSource;

/**
 * MybatisConfig class
 * description: MybatisConfig
 *
 * @author Administrator
 * @date 2023/8/3
 */
public class MybatisConfig {

    // 获取SqlSessionFactory
    @Bean
    public SqlSessionFactoryBean getSqlSessionFactoryBean(DataSource dataSource) {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        // 扫描entity包
        sqlSessionFactoryBean.setTypeAliasesPackage("com.spark.entity");
        return sqlSessionFactoryBean;
    }

    // 映射dao
    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer(){
        MapperScannerConfigurer configurer = new MapperScannerConfigurer();
        configurer.setBasePackage("com.spark.dao");
        return configurer;
    }
}

编写Spring和SpringMVC配置类

package com.spark.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;

/**
 * SpringConfig class
 * description: SpringConfig
 *
 * @author Administrator
 * @date 2023/8/3
 */
@Configuration
@PropertySource({"classpath:jdbc.properties"})
@Import({JdbcConfig.class,MybatisConfig.class})
@ComponentScan({"com.spark.service","com.spark.dao"})
public class SpringConfig {
}

package com.spark.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

/**
 * SpringMvcConfig class
 * description: SpringMvcConfig
 *
 * @author Administrator
 * @date 2023/8/3
 */
@Configuration
@ComponentScan("com.spark.controller")
@EnableWebMvc
public class SpringMvcConfig {
}

编写Servlet容器配置

package com.spark.config;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

/**
 * ServletConfig class
 * description: ServletConfig
 *
 * @author Administrator
 * @date 2023/8/3
 */
public class ServletConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[]{SpringConfig.class};
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{SpringMvcConfig.class};
    }

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

功能模块开发 

数据库创建数据表,并写入数据

DROP TABLE IF EXISTS `books`;
CREATE TABLE `books`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `type` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `description` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 13 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

INSERT INTO `books` VALUES (1, '计算机理论', 'Spring实战 第5版', 'Spring入门经典教程,深入理解Spring原理技术内幕');
INSERT INTO `books` VALUES (2, '计算机理论', 'Spring 5核心原理与30个类手写实战', '十年沉淀之作,手写Spring精华思想');
INSERT INTO `books` VALUES (3, '计算机理论', 'Spring 5 设计模式', '深入Spring源码剖析Spring源码中蕴含的10大设计模式');
INSERT INTO `books` VALUES (4, '计算机理论', 'Spring MVC+MyBatis开发从入门到项目实战', '全方位解析面向Web应用的轻量级框架,带你成为Spring MVC开发高手');
INSERT INTO `books` VALUES (5, '计算机理论', '轻量级Java Web企业应用实战', '源码级剖析Spring框架,适合已掌握Java基础的读者');
INSERT INTO `books` VALUES (6, '计算机理论', 'Java核心技术 卷I 基础知识(原书第11版)', 'Core Java 第11版,Jolt大奖获奖作品,针对Java SE9、10、11全面更新');
INSERT INTO `books` VALUES (7, '计算机理论', '深入理解Java虚拟机', '5个维度全面剖析JVM,大厂面试知识点全覆盖');
INSERT INTO `books` VALUES (8, '计算机理论', 'Java编程思想(第4版)', 'Java学习必读经典,殿堂级著作!赢得了全球程序员的广泛赞誉');
INSERT INTO `books` VALUES (9, '计算机理论', '零基础学Java(全彩版)', '零基础自学编程的入门图书,由浅入深,详解Java语言的编程思想和核心技术');
INSERT INTO `books` VALUES (10, '市场营销', '直播就该这么做:主播高效沟通实战指南', '李子柒、李佳琦、薇娅成长为网红的秘密都在书中');
INSERT INTO `books` VALUES (11, '市场营销', '直播销讲实战一本通', '和秋叶一起学系列网络营销书籍');
INSERT INTO `books` VALUES (12, '市场营销', '直播带货:淘宝、天猫直播从新手到高手', '一本教你如何玩转直播的书,10堂课轻松实现带货月入3W+');

创建实体类,并编写Dao 

package com.spark.entity;

/**
 * Book class
 * description: Book
 *
 * @author Administrator
 * @date 2023/8/3
 */
public class Book {

    private Integer id;

    private String type;

    private String name;

    private String description;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    @Override
    public String toString() {
        return "Book{" +
                "id=" + id +
                ", type='" + type + '\'' +
                ", name='" + name + '\'' +
                ", description='" + description + '\'' +
                '}';
    }
}
package com.spark.dao;

import com.spark.entity.Book;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;

import java.util.List;

/**
 * BookDao class
 * description: BookDao
 *
 * @author Administrator
 * @date 2023/8/3
 */
public interface BookDao {

    @Insert("insert into books(`type`,`name`,`description`) values(#{type},#{name},#{description})")
    int save(Book book);
    @Update("update books set type=#{type}, name=#{name}, description=#{description} where id = #{id}")
    int update(Book book);
    @Delete("delete from books where id = #{id}")
    int delete(Integer id);
    @Select("select * from books where id = #{id}")
    Book getById(Integer id);
    @Select("select * from books")
    List<Book> getAll();
}

定义业务层接口方法并编写实现类

package com.spark.service;

import com.spark.entity.Book;

import java.util.List;

/**
 * BookService class
 * description: BookService
 *
 * @author Administrator
 * @date 2023/8/3
 */
public interface BookService {

    boolean addBook(Book book);
    boolean updateBook(Book book);
    boolean deleteBook(Integer id);
    Book selectById(Integer id);
    List<Book> selectAll();
}
package com.spark.service.impl;

import com.spark.dao.BookDao;
import com.spark.entity.Book;
import com.spark.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * BookServiceImpl class
 * description: BookServiceImpl
 *
 * @author Administrator
 * @date 2023/8/3
 */
@Service
public class BookServiceImpl implements BookService {

    @Autowired
    BookDao bookDao;

    @Override
    public boolean addBook(Book book) {
        return bookDao.save(book) > 0;
    }

    @Override
    public boolean updateBook(Book book) {
        return bookDao.update(book) > 0;
    }

    @Override
    public boolean deleteBook(Integer id) {
        return bookDao.delete(id) > 0;
    }

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

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

编写表现层Controller

package com.spark.controller;

import com.spark.entity.Book;
import com.spark.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * BookController class
 * description: BookController
 *
 * @author Administrator
 * @date 2023/8/3
 */
@RestController
@RequestMapping("/books")
public class BookController {

    @Autowired
    private BookService bookService;

    @PostMapping("/addBook")
    public String addBook(@RequestBody Book book){
        if(bookService.addBook(book)){
            return "添加成功";
        }else{
            return "添加失败";
        }
    }
    @PutMapping("/updateBook")
    public String updateBook(@RequestBody Book book){
        if(bookService.updateBook(book)){
            return "更新成功";
        }else{
            return "更新失败";
        }
    }
    @DeleteMapping("/deleteBook")
    public String deleteBook(Integer id){
        if(bookService.deleteBook(id)){
            return "删除成功";
        }else{
            return "删除失败";
        }
    }
    @GetMapping("/getById")
    public Book selectById(Integer id){
        return bookService.selectById(id);
    }
    @GetMapping("/getAll")
    public List<Book> selectAll(){
        return bookService.selectAll();
    }

}

接口测试

编写测试类

package com.spark;

import com.spark.config.SpringConfig;
import com.spark.entity.Book;
import com.spark.service.BookService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * BookServiceTest class
 * description: BookServiceTest
 *
 * @author Administrator
 * @date 2023/8/5
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class BookServiceTest {

    @Autowired
    BookService bookService;

    @Test
    public void testBookInsert(){
        Book book = new Book();
        book.setName("Java从入门到放弃");
        book.setType("计算机");
        book.setDescription("Java从入门到放弃");
        bookService.addBook(book);
    }

    @Test
    public void testBookUpdate(){
        Book book = new Book();
        book.setName("Java从入门到放弃 第二版");
        book.setType("计算机");
        book.setDescription("Java从入门到放弃第二版");
        book.setId(13);
        bookService.updateBook(book);
    }

    @Test
    public void testBookDelete(){
        bookService.deleteBook(13);
    }

    @Test
    public void testBookSelectById(){
        System.out.println(bookService.selectById(1));
    }

    @Test
    public void testBookSelectAll(){
        System.out.println(bookService.selectAll());
    }
}

拓展:开启事务

Spring配置类中使用注解开启事务管理

package com.spark.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;
import org.springframework.transaction.annotation.EnableTransactionManagement;

/**
 * SpringConfig class
 * description: SpringConfig
 *
 * @author Administrator
 * @date 2023/8/3
 */
@Configuration
@PropertySource({"classpath:jdbc.properties"})
@Import({JdbcConfig.class,MybatisConfig.class})
@ComponentScan({"com.spark.service","com.spark.dao"})
// 开启事务
@EnableTransactionManagement
public class SpringConfig {
}

在Jdbc配置类中获取事务管理对象

package com.spark.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;

/**
 * JdbcConfig class
 * description: JdbcConfig
 *
 * @author Administrator
 * @date 2023/8/3
 */
public class JdbcConfig {

    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;

    // 注册数据源
    @Bean
    public DataSource getDataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return dataSource;
    }

    // 数据源事务管理对象
    @Bean
    public DataSourceTransactionManager transactionManager(DataSource dataSource){
        DataSourceTransactionManager ds = new DataSourceTransactionManager();
        ds.setDataSource(dataSource);
        return ds;
    }
}

在业务层使用事务管理注解

package com.spark.service;

import com.spark.entity.Book;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * BookService class
 * description: BookService
 *
 * @author Administrator
 * @date 2023/8/3
 */
@Transactional
public interface BookService {

    boolean addBook(Book book);
    boolean updateBook(Book book);
    boolean deleteBook(Integer id);
    Book selectById(Integer id);
    List<Book> selectAll();
}

表现层与前端数据传输

由于数据传输可能会有多种格式,前端需要分别去解析数据,因此与前端交互时一般需要统一格式。创建结果模型类,封装数据到data属性中,封装操作结果到code属性中,封装特殊消息到message(msg)属性中

public class Result{
    private String code;
    private String msg;
    private Object data;
}

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

定义统一结果类

package com.spark.util;

/**
 * Result class
 * description: Result
 *
 * @author Administrator
 * @date 2023/8/5
 */
public class Result {
    
    private String code;
    
    private String msg;
    
    private Object data;

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

    public String getCode() {
        return code;
    }

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

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }
}

定义结果类所需的常量

package com.spark.common;

/**
 * RestCommonContent class
 * description: RestCommonContent
 *
 * @author Administrator
 * @date 2023/8/5
 */
public class RestCommonContent {

    // 定义响应编码和响应信息
    public static final String RESPONSE_SUCCESS = "操作成功!";
    public static final String RESPONSE_ERROR = "操作失败!";
    public static final String RESPONSE_SUCCESS_CODE = "200";
    public static final String RESPONSE_ERROR_CODE = "9999";
    public static final String SELECT_SUCCESS = "查询成功!";
    public static final String SELECT_ERROR = "数据不存在,查询失败!";
    
    // 定义数据常量
    public static final String CONTENT_EMPTY = "";
    public static final String CONTENT_NULL = null;
    
}

 改写表现层Controller

package com.spark.controller;

import com.spark.common.RestCommonContent;
import com.spark.entity.Book;
import com.spark.service.BookService;
import com.spark.util.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * BookController class
 * description: BookController
 *
 * @author Administrator
 * @date 2023/8/3
 */
@RestController
@RequestMapping("/books")
public class BookController {

    @Autowired
    private BookService bookService;

    @PostMapping("/addBook")
    public Result addBook(@RequestBody Book book){
        if(bookService.addBook(book)){
            return new Result(RestCommonContent.RESPONSE_SUCCESS_CODE,
                    RestCommonContent.RESPONSE_SUCCESS,
                    true);
        }else{
            return new Result(RestCommonContent.RESPONSE_ERROR_CODE,
                    RestCommonContent.RESPONSE_ERROR,
                    false);
        }
    }
    @PutMapping("/updateBook")
    public Result updateBook(@RequestBody Book book){
        if(bookService.updateBook(book)){
            return new Result(RestCommonContent.RESPONSE_SUCCESS_CODE,
                    RestCommonContent.RESPONSE_SUCCESS,
                    true);
        }else{
            return new Result(RestCommonContent.RESPONSE_ERROR_CODE,
                    RestCommonContent.RESPONSE_ERROR,
                    false);
        }
    }
    @DeleteMapping("/deleteBook")
    public Result deleteBook(Integer id){
        if(bookService.deleteBook(id)){
            return new Result(RestCommonContent.RESPONSE_SUCCESS_CODE,
                    RestCommonContent.RESPONSE_SUCCESS,
                    true);
        }else{
            return new Result(RestCommonContent.RESPONSE_ERROR_CODE,
                    RestCommonContent.RESPONSE_ERROR,
                    false);
        }
    }
    @GetMapping("/getById")
    public Result selectById(Integer id){
        Book book = bookService.selectById(id);
        if(ObjectUtils.isEmpty(book)){
            return new Result(RestCommonContent.RESPONSE_ERROR_CODE,
                    RestCommonContent.SELECT_ERROR,
                    RestCommonContent.CONTENT_EMPTY);
        }
        return new Result(RestCommonContent.RESPONSE_SUCCESS_CODE,
                RestCommonContent.SELECT_SUCCESS,
                book);
    }
    @GetMapping("/getAll")
    public Result selectAll(){
        List<Book> books = bookService.selectAll();
        if(ObjectUtils.isEmpty(books)){
            return new Result(RestCommonContent.RESPONSE_SUCCESS_CODE,
                    RestCommonContent.SELECT_SUCCESS,
                    RestCommonContent.CONTENT_EMPTY);
        }
        return new Result(RestCommonContent.RESPONSE_SUCCESS_CODE,
                RestCommonContent.SELECT_SUCCESS,
                books);
    }

}

异常处理器

开发中避免不了会遇到异常现象,如果不做处理,前端页面获取错误信息时拿到的是错误页面,无法进行解析,此时需要对异常进行统一处理

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

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

Spring提供了异常处理器,集中的、统一的处理项目中出现的异常

@RestControllerAdvice
public class ProjectExceptionAdvice{
    @ExceptionHandler(Exception.class)
    public Result doException(Exception ex){
		// 处理异常返回结果
    }
}

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

项目异常分类

业务异常(BusinessException)

  • 不规范的用户行为产生的异常 

系统异常(SystemException)

  • 项目运行过程中可预计且无法避免的异常 

其他异常(Exception)

  • 编程人员未预期的异常 

项目异常处理

编写业务异常类和系统异常类

package com.spark.exception;

/**
 * BusinessException class
 * description: BusinessException
 *
 * @author Administrator
 * @date 2023/8/5
 */
public class BusinessException extends RuntimeException{
    
    private String code; // 错误编码
    
    private String tranCode; // 交易码(接口号)
    

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

    public BusinessException(String code,String message, String tranCode, Throwable cause) {
        super(message, cause);
        this.code = code;
        this.tranCode = tranCode;
    }

    public String getCode() {
        return code;
    }

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

    public String getTranCode() {
        return tranCode;
    }

    public void setTranCode(String tranCode) {
        this.tranCode = tranCode;
    }
}
package com.spark.exception;

/**
 * SystemException class
 * description: SystemException
 *
 * @author Administrator
 * @date 2023/8/5
 */
public class SystemException extends RuntimeException {

    private String code; // 错误编码

    private String tranCode; // 交易码(接口号)


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

    public SystemException(String code,String message, String tranCode, Throwable cause) {
        super(message, cause);
        this.code = code;
        this.tranCode = tranCode;
    }

    public String getCode() {
        return code;
    }

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

    public String getTranCode() {
        return tranCode;
    }

    public void setTranCode(String tranCode) {
        this.tranCode = tranCode;
    }
}

自定义异常错误编码和错误信息 

package com.spark.common;

/**
 * ExceptionContent class
 * description: ExceptionContent
 *
 * @author Administrator
 * @date 2023/8/5
 */
public class ExceptionContent {

    public static final String BUSINESS_ERROR_CODE = "50001";
    public static final String BUSINESS_DATA_ERROR = "数据有误,请检查您的数据格式是否正确!";
    public static final String BUSINESS_ERROR_MSG = "操作有误,请联系管理员!";
    
    public static final String SYSTEM_ERROR_CODE = "50002";
    public static final String SYSTEM_ERROR_MSG = "服务器异常,请您稍后重试!";
    
    public static final String SYSTEM_UNKNOWN_ERROR_CODE = "50003";
    public static final String SYSTEM_UNKNOWN_MSG = "系统繁忙,请联系管理员!";
}

改写业务层根据id查询Book方法,处理异常

@Override
public Book selectById(Integer id) {
    if(id<=0){
        throw new BusinessException(ExceptionContent.BUSINESS_ERROR_CODE,
                                    ExceptionContent.BUSINESS_DATA_ERROR,
                                    "BK1004",
                                    new Throwable(ExceptionContent.BUSINESS_DATA_ERROR));
    }
    return bookDao.getById(id);
}

编写异常处理器统一处理异常

package com.spark.exception;

import com.spark.common.ExceptionContent;
import com.spark.util.Result;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

/**
 * ProjectExceptionAdvice class
 * description: ProjectExceptionAdvice
 *
 * @author Administrator
 * @date 2023/8/5
 */
@RestControllerAdvice
public class ProjectExceptionAdvice {

    // 处理业务异常
    @ExceptionHandler
    public Result doBusinessException(BusinessException ex){
        // 记录日志
        System.out.println(ex.getTranCode() + "出现异常");
        return new Result(ex.getCode(),ex.getMessage(),null);
    }

    // 处理系统异常
    @ExceptionHandler
    public Result doSystemException(SystemException sy){
        // 记录日志
        System.out.println(sy.getTranCode() + "出现异常");
        return new Result(sy.getCode(),sy.getMessage(),null);
    }
    
    // 处理其他未知异常
    @ExceptionHandler
    public Result doException(Exception ex){
        // 记录日志
        System.out.println("出现未知异常");
        System.out.println("异常信息如下:");
        System.out.println(ex.getMessage());
        return new Result(ExceptionContent.SYSTEM_UNKNOWN_ERROR_CODE,
                ExceptionContent.SYSTEM_UNKNOWN_MSG,
                null);
    }
}

前后台联调 

由于后台Servlet容器配置里面是拦截一切请求,前端代码在项目的webapp目录下,Tomcat启动后,去访问前端页面会被拦截,因此需要对前端资源进行放行

编写SpringMVC资源放行配置类

package com.spark.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

/**
 * SpringMvcSupportConfig class
 * description: SpringMvcSupportConfig
 *
 * @author Administrator
 * @date 2023/8/5
 */
@Configuration
public class SpringMvcSupportConfig extends WebMvcConfigurationSupport {

    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
        registry.addResourceHandler("/css/**").addResourceLocations("/css/");
        registry.addResourceHandler("/js/**").addResourceLocations("/js/");
        registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
    }
}

在SpringMVC配置类中扫描config包

package com.spark.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

/**
 * SpringMvcConfig class
 * description: SpringMvcConfig
 *
 * @author Administrator
 * @date 2023/8/3
 */
@Configuration
@ComponentScan({"com.spark.controller","com.spark.config"})
@EnableWebMvc
public class SpringMvcConfig {
}

 拦截器

概述 

拦截器是一种动态拦截方法调用的机制,在SpringMVC中动态拦截控制器方法的执行

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

拦截器和过滤器的区别

  • 归属不同。Filter属于Servlet技术,Inteceptor属于SpringMVC技术
  • 拦截内容不同。Filter对所有访问进行增强,Inteceptor仅对SpringMVC的访问进行增强 

入门案例 

编写拦截器

package com.spark.interceptor;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * ProjectInteceptor class
 * description: ProjectInteceptor
 *
 * @author Administrator
 * @date 2023/8/7
 */
@Component
public class ProjectInteceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("原始方法执行前执行...");
        return true;
    }

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

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("postHandle执行后执行...");
    }
}

SpringMVC配置类中添加拦截器并扫描

package com.spark.config;

import com.spark.interceptor.ProjectInteceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

/**
 * SpringMvcSupportConfig class
 * description: SpringMvcSupportConfig
 *
 * @author Administrator
 * @date 2023/8/5
 */
@Configuration
public class SpringMvcSupportConfig extends WebMvcConfigurationSupport {

    @Autowired
    ProjectInteceptor projectInteceptor;

    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
        registry.addResourceHandler("/css/**").addResourceLocations("/css/");
        registry.addResourceHandler("/js/**").addResourceLocations("/js/");
        registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
    }
	
    // 添加拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(projectInteceptor).addPathPatterns("/books/*","/books");
    }
}
package com.spark.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

/**
 * SpringMvcConfig class
 * description: SpringMvcConfig
 *
 * @author Administrator
 * @date 2023/8/3
 */
@Configuration
@ComponentScan({"com.spark.controller","com.spark.config","com.spark.interceptor"})
@EnableWebMvc
public class SpringMvcConfig{

}

拦截器链 

当配置多个拦截器时,形成拦截器链

多拦截器执行顺序

  • 当配置多个拦截器时,形成拦截器链
  • 拦截器链的运行顺序参照拦截器添加顺序为准
  • 当拦截器中出现对原始处理器的拦截,后面的拦截器均终止执行
  • 当拦截器运行中断,仅运行配置在前面的拦截器的afterCompletion操作 

preHandle方法按照拦截器配置顺序执行,postHandle和afterCompletion方法按照配置顺序倒序执行 

工作流程

  1. 用户发送请求至前端控制器DispatcherServlet
  2. DispatcherServlet收到请求后,调用HandlerMapping处理器映射器,请求获取Handle
  3. 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet
  4. DispatcherServlet调用HandlerAdapter处理器适配器
  5. HandlerAdapter经过适配调用具体处理器(Handler,也叫后端控制器)
  6. Handler执行完毕后返回ModelAndView
  7. HandlerAdapter将Handler执行结果ModelAndView返回给DispatcherServlet
  8. DispatcherServlet将ModelAndView传给ViewResolver视图解析器进行解析
  9. ViewResolver解析后返回具体View
  10. DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)
  11. DispatcherServlet响应用户 
  • 20
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值