SpringMVC框架学习2

1.SSM整合

1.1 流程分析
(1) 创建工程
创建一个 Maven web 工程
pom.xml 添加 SSM 需要的依赖 jar
编写 Web 项目的入口配置类,实现 AbstractAnnotationConfigDispatcherServletInitializer 重写以下方法:
get Root ConfigClasses() :返回 Spring 的配置类 -> 需要 SpringConfig 配置类
get Servlet ConfigClasses() :返回 SpringMVC 的配置类 -> 需要 SpringMvcConfig 配置类
get ServletMappings() : 设置 SpringMVC 请求拦截路径规则
get ServletFilters() :设置 过滤器,解决POST请求中文乱码问题
(2)SSM 整合 [ 重点是各个配置的编写 ]
SpringConfig
标识该类为配置类 @Configuration
扫描 Service 所在的包 @ComponentScan
Service 层要管理事务 @EnableTransactionManagement
读取外部的 properties 配置文件 @PropertySource
整合 Mybatis 需要引入 Mybatis 相关配置类 @Import
第三方数据源配置类 JdbcConfig
构建 DataSource 数据源, DruidDataSouroce, 需要注入数据库连接四要素, @Bean @Value
构建平台事务管理器, DataSourceTransactionManager,@Bean Mybatis 配置类 MybatisConfig
构建 SqlSessionFactoryBean 并设置别名扫描与数据源, @Bean
构建 MapperScannerConfigurer 并设置 DAO 层的包扫描
SpringMvcConfig
标识该类为配置类 @Configuration
扫描 Controller 所在的包 @ComponentScan
开启 SpringMVC 注解支持 @EnableWebMvc
(3) 功能模块 [ 与具体的业务模块有关 ]
创建数据库表
根据数据库表创建对应的模型类
通过 Dao 层完成数据库表的增删改查 ( 接口 + 自动代理 )
编写 Service [Service 接口 + 实现类 ]
@Service
@Transactional
整合Junit对业务层进行单元测试
@RunWith
@ContextConfiguration
@Test
编写 Controller
接收请求 @RequestMapping @GetMapping @PostMapping @PutMapping
@DeleteMapping
接收数据 简单、 POJO 、嵌套 POJO 、集合、数组、 JSON 数据类型
@RequestParam
@PathVariable
@RequestBody
转发业务层
@Autowired
响应结果
@ResponseBody
1.创建SpringConfig配置类
package com.itheima.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;

/**
 * Created by IntelliJ IDEA.
 *
 * @Author : Runqiang Jiang
 * @create 2022/5/22 10:14
 */
@Configuration
@ComponentScan({"com.itheima.service"})
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MybatisConfig.class})
@EnableTransactionManagement
public class SpringConfig {
}
2.Jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm_db
jdbc.username=root
jdbc.password=169527
3.创建 JdbcConfig 配置类
package com.itheima.config;

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

import javax.sql.DataSource;

/**
 * Created by IntelliJ IDEA.
 *
 * @Author : Runqiang Jiang
 * @create 2022/5/22 10:13
 */
@Configuration
public class JdbcConfig {
    // 可以调用的原因,Spring已经加载了
    //@PropertySource("classpath:jdbc.properties")
    @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 // 注册为bean类,以使Spring进行管理
    public DataSource dataSource(){
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName(driver);
        druidDataSource.setUrl(url);
        druidDataSource.setUsername(username);
        druidDataSource.setPassword(password);
        return druidDataSource;
    }

    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource){
        DataSourceTransactionManager ds=new DataSourceTransactionManager();
        ds.setDataSource(dataSource);
        return ds;
    }

}

4.创建MybatisConfig配置类

package com.itheima.config;

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

import javax.sql.DataSource;

/**
 * Created by IntelliJ IDEA.
 *
 * @Author : Runqiang Jiang
 * @create 2022/5/22 10:13
 */

public class MybatisConfig {


    @Bean
    public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){

        SqlSessionFactoryBean factoryBean=new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        factoryBean.setTypeAliasesPackage("co.itheima.entity");
        return factoryBean;
    }
    @Bean // 注册到Spring后方法的名称就会变黄高亮
    public MapperScannerConfigurer mapperScannerConfigurer(){
        MapperScannerConfigurer mapperScannerConfigurer=new MapperScannerConfigurer();
        mapperScannerConfigurer.setBasePackage("com.itheima.dao");
        return mapperScannerConfigurer;
    }
}

5.创建SpringMVC配置类

package com.itheima.config;

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

/**
 * Created by IntelliJ IDEA.
 *
 * @Author : Runqiang Jiang
 * @create 2022/5/22 10:13
 */
@Configuration
@ComponentScan("com.itheima.controller")
@EnableWebMvc
public class SpringMVCConfig {
}

:创建Web项目入口配置类

package com.itheima.config;

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

/**
 * Created by IntelliJ IDEA.
 *
 * @Author : Runqiang Jiang
 * @create 2022/5/22 15:59
 */
public class ServletConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
    // 对于root加载的配置类
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[]{ SpringConfig.class};
    }
    //对于Web容器加载的配置类
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{SpringMVCConfig.class};
    }

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

功能模块开发

1.创建数据库以及表

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

2 编写模型类

package com.itheima.entity;

/**
 * Created by IntelliJ IDEA.
 *
 * @Author : Runqiang Jiang
 * @create 2022/5/22 16:11
 */
public class Book {
    private Integer id;
    private String type;
    private String name;
    private String description;

    public Book() {
    }

    public Book(Integer id, String type, String name, String description) {
        this.id = id;
        this.type = type;
        this.name = name;
        this.description = description;
    }

    @Override
    public String toString() {
        return "Book{" +
                "id=" + id +
                ", type='" + type + '\'' +
                ", name='" + name + '\'' +
                ", description='" + 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;
    }
}
3: 编写 Dao 接口
package com.itheima.dao;

import com.itheima.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;

/**
 * Created by IntelliJ IDEA.
 *
 * @Author : Runqiang Jiang
 * @create 2022/5/22 16:14
 */
public interface BookDao {
    // 写法1
    // @Insert("insert into tbl_book values(null,#{type},#{name},#{description})")
    //写法2 第一个type是表的字段名,第二个type.#{type}是book的属性
    @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);
    @Select("select * from tbl_book where id=#{id}")
    public Book getById(Integer id);
    @Select("select * from tbl_book")
    public List<Book> getAll();
}
4: 编写 Service 接口和实现类
BookService:
package com.itheima.service;

import com.itheima.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;

/**
 * Created by IntelliJ IDEA.
 *
 * @Author : Runqiang Jiang
 * @create 2022/5/22 16:15
 */
@Transactional
public interface BookService {
    /**
     * 保存
     * @param book
     * @return
     */
    public boolean save(Book book);

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

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

    /**
     * 根据ID查询
     * @param id
     * @return
     */
    public Book getById(Integer id);

    /**
     * 查询所有
     * @return
     */
    public List<Book> getAll();
}

BookServiceImpl

package com.itheima.service.impl;

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

import java.util.List;

/**
 * Created by IntelliJ IDEA.
 *
 * @Author : Runqiang Jiang
 * @create 2022/5/22 16:15
 */
@Service
public class BookServiceImpl implements BookService {
    @Autowired
    private BookDao bookDao;

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

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

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

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

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

编写Contorller

package com.itheima.controller;

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

import java.util.List;

/**
 * Created by IntelliJ IDEA.
 *
 * @Author : Runqiang Jiang
 * @create 2022/5/22 16:15
 */
@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("id") Integer id){
        return bookService.delete(id);
    }
    @GetMapping("/{id}")
    public Book getById(@PathVariable("id") Integer id){
        return bookService.getById(id);
    }
    @GetMapping
    public List<Book> getAll(){
        return bookService.getAll();
    }

}

6  单元测试

package com.itheima.service;

import com.itheima.config.SpringConfig;
import com.itheima.entity.Book;
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;

import java.util.List;

/**
 * Created by IntelliJ IDEA.
 *
 * @Author : Runqiang Jiang
 * @create 2022/5/22 20:50
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class BookServiceTest {
    @Autowired
    private BookService bookService;
    @Test
    public void testGetById(){
        Book book = bookService.getById(1);
        System.out.println(book);
    }
    @Test
    public void testGetAll(){
        List<Book> books = bookService.getAll();
       books.forEach(book -> {
           System.out.println(book);
       });
    }
}
7 PostMan 测试
新增

 修改

删除

查询单个

查询所有

2 ,统一结果封装
2.1 表现层与前端数据传输协议定义
SSM 整合以及功能模块开发完成后,接下来,我们在上述案例的基础上分析下有哪些问题需要我们去解决下。首先第一个问题是:
目前我们就已经有三种数据类型返回给前端,如果随着业务的增长,我们需要返回的数据类型会越来越多。对于前端开发人员在解析数据的时候就比较凌乱了,所以对于前端来说,如果后台能够返回一个统一的数据结果,前端在解析的时候就可以按照一种方式进行解析。开发就会变得更加简单。所以我们就想能不能将返回结果的数据进行统一,具体如何来做,大体的思路为:
为了封装返回的结果数据 : 创建结果模型类,封装数据到 data 属性中
为了封装返回的数据是何种操作及是否操作成功 : 封装操作结果到 code 属性中
操作失败后为了封装返回的错误信息 : 封装特殊消息到 message(msg) 属性中

 

 结果封装

package com.itheima.controller;

/**
 * Created by IntelliJ IDEA.
 *
 * @Author : Runqiang Jiang
 * @create 2022/5/22 21:42
 */
public class Result {

    private Object data;
    private Integer code;
    private String msg;

    public Result() {
    }

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

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

    public Object getData() {
        return data;
    }

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

    public Integer getCode() {
        return code;
    }

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

    public String getMsg() {
        return msg;
    }

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

状态码

package com.itheima.controller;

/**
 * Created by IntelliJ IDEA.
 *
 * @Author : Runqiang Jiang
 * @create 2022/5/22 21:45
 */
// 状态码
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;
}

修改Controller中的返回值:

package com.itheima.controller;

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

import java.util.List;

/**
 * Created by IntelliJ IDEA.
 *
 * @Author : Runqiang Jiang
 * @create 2022/5/22 16:15
 */
@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("id") Integer id){
        boolean delete = bookService.delete(id);
        return new Result(delete?Code.DELETE_OK:Code.DELETE_ERR,delete);
    }
    @GetMapping("/{id}")
    public Result getById(@PathVariable("id") Integer id){
        Book book = bookService.getById(id);
       Integer code= book!=null?Code.GET_OK:Code.GET_ERR;
       String msg= book!=null?"":"数据查询失败,请重试";
        return  new Result(book,code,msg);
    }
    @GetMapping
    public Result getAll(){
        List<Book> books = bookService.getAll();
        Integer code= books!=null?Code.GET_OK:Code.GET_ERR;
        String msg= books!=null?"":"数据查询失败,请重试";
        return  new Result(books,code,msg);
    }

}

Postman工具测试:

添加单个图书成功:

查询单个成功 

查询单个失败:

查询所有

 3,统一异常处理

3.1 问题描述
先来演示个效果,修改 BookController 类的 getById 方法

    @GetMapping("/{id}")
    public Result getById(@PathVariable("id") Integer id) {
        //手动添加一个错误信息 
        if (id == 1) {
            int i = 1 / 0;
        }
        Book book = bookService.getById(id);
        Integer code = book != null ? Code.GET_OK : Code.GET_ERR;
        String msg = book != null ? "" : "数据查询失败,请重试";
        return new Result(book, code, msg);
    }

当postman查询id=1的结果的时候:

 前端接收到这个信息后和之前约定的格式不一致,这个问题该如何解决?在解决问题之前,先来看下异常的种类及出现异常的原因:

框架内部抛出的异常:因使用不合规导致
数据层抛出的异常:因外部服务器故障导致(例如:服务器访问超时)
业务层抛出的异常:因业务逻辑书写错误导致(例如:遍历业务书写操作,导致索引异常等)
表现层抛出的异常:因数据收集、校验等规则导致(例如:不匹配的数据类型间导致异常)
工具类抛出的异常:因工具类书写不严谨不够健壮导致(例如:必要释放的连接长期未释放等)
看完上面这些出现异常的位置,会发现在开发的任何一个位置都有可能出现异常,而且这些 异常是不能避免的。所以就得将异常进行处理。思考
1. 各个层级均出现异常,异常处理代码书写在哪一层 ?
所有的异常均抛出到表现层进行处理
2. 异常的种类很多,表现层如何将所有的异常都处理到呢 ?
异常分类
3. 表现层处理异常,每个方法中单独书写,代码书写量巨大且意义不强,如何解决 ?
AOP
对于上面这些问题及解决方案, SpringMVC 已经提供了一套解决方案 : 异常处理器( 集中的、统一的处理项目中出现的异常)。
代码说明:
package com.itheima.controller;

import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

/**
 * Created by IntelliJ IDEA.
 *
 * @Author : Runqiang Jiang
 * @create 2022/6/5 15:02
 */
@RestControllerAdvice
public class ProjectExceptionAdvice {
    @ExceptionHandler(Exception.class)
    public Result doException(Exception ex){
        System.out.println("嘿嘿,异常哪里跑!!");
        return new Result(666,null);


    }
}
异常访问的结果:

 知识点:

 

3.2  项目异常处理方案
3.2.1 异常分类
异常处理器已经能够使用了,那么在项目中该如何来处理异常呢 ?
因为异常的种类有很多,如果每一个异常都对应一个 @ExceptionHandler ,那得写多少个方法来处
理各自的异常,所以我们在处理异常之前,需要对异常进行一个分类 :
业务异常(BusinessException)
规范的用户行为产生的异常:用户在页面输入内容的时候未按照指定格式进行数据填写,如在年龄框输入的是字符串 不规范的用户行为操作产生的异常
系统异常(SystemException) 项目运行过程中可预计但无法避免的异常,比如数据库或服务器宕机
其他异常(Exception)
编程人员未预期到的异常,如 : 用到的文件不存在
3.2.2 异常解决方案
业务异常( BusinessException
发送对应消息传递给用户,提醒规范操作。 大家常见的就是提示用户名已存在或密码格式不正确等
系统异常( SystemException
发送固定消息传递给用户,安抚用户
系统繁忙,请稍后再试。系统正在维护升级,请稍后再试 。系统出问题,请联系系统管理员等
发送特定消息给运维人员,提醒维护。可以发送短信、邮箱或者是公司内部通信软件 。记录日志
发消息和记录日志对用户来说是不可见的,属于后台程序
其他异常(Exception
发送固定消息传递给用户,安抚用户。发送特定消息给编程人员,提醒维护(纳入预期范围内)
一般是程序没有考虑全,比如未做非空校验等。记录日志
3.2.3 异常解决方案的具体实现
思路:
1.先通过自定义异常,完成BusinessExceptionSystemException的定义
2.将其他异常包装成自定义异常类型
3.在异常处理器类中对不同的异常进行处理
具体实现:
步骤 1: 自定义异常类
package com.itheima.exception;

/**
 * Created by IntelliJ IDEA.
 *
 * @Author : Runqiang Jiang
 * @create 2022/6/5 15:21
 */
public class BusinessException extends RuntimeException{
    private Integer code;



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

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

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }
}
package com.itheima.exception;

/**
 * Created by IntelliJ IDEA.
 *
 * @Author : Runqiang Jiang
 * @create 2022/6/5 15:21
 */
public class SystemException extends RuntimeException{
    private Integer code;



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

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

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }
}
步骤 2: 将其他异常包成自定义异常
  @Override
    public Book getById(Integer id) {
        if(id==1){
            throw  new BusinessException(Code.BUSINESS_ERR,"请不要用你的技术挑战我的耐性");
        }
        //将可能出现的异常进行自定义包装,转换为自定义异常
        try{
            int i=1/0;
        }catch (Exception e){
              throw  new SystemException(Code.SYSTEM_ERR,"服务器访问超时,请重试",e);
        }

        return bookDao.getById(id);
    }
具体的包装方式有:
方式一: try{}catch(){}catch中重新throw我们自定义异常即可。
方式二:直接throw自定义异常即可
步骤 3: 处理器类中处理自定义异常
package com.itheima.controller;

import com.itheima.exception.BusinessException;
import com.itheima.exception.SystemException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

/**
 * Created by IntelliJ IDEA.
 *
 * @Author : Runqiang Jiang
 * @create 2022/6/5 15:02
 */
//@RestControllerAdvice用于标识当前类为REST风格对应的异常处理器
@RestControllerAdvice
public class ProjectExceptionAdvice {

    @ExceptionHandler(SystemException.class)
    public Result doSystemException(SystemException ex) {
        // 记录日志
        //发送消息给运维
        //发送邮件给开发人员
        return new Result(ex.getCode(), null, ex.getMessage());
    }

    @ExceptionHandler(BusinessException.class)
    public Result doBusinessException(BusinessException ex) {
        return new Result(ex.getCode(), null, ex.getMessage());

    }

    @ExceptionHandler(Exception.class)
    public Result doException (Exception ex){
        // 记录日志
        //发送消息给运维
        //发送邮件给开发人员
        System.out.println("嘿嘿,异常哪里跑!!");
        return new Result(Code.SYSTEM_UNKNOWN_ERR, null,"系统繁忙,请稍后再试");


    }
}
步骤 4: 运行程序
如果传入参数ID=1 ,会报 BusinessException

如果传入的是其他参数,会报SystemException

 

对于异常就已经处理完成了,不管后台哪一层抛出异常,都会以与前端约定好的方式进行返回,前端只需要把信息获取到,根据返回的正确与否来展示不同的内容即可。
小结
以后项目中的异常处理方式为 :

 4,前后台协议联调

4.1 环境准备

加载前台页面到Webapp后,因为添加了静态资源,SpringMVC会拦截,所有需要在SpringConfig的配置类中将静态资源进行放行。新建SpringMvcSupport:

package com.itheima.config;

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

/**
 * Created by IntelliJ IDEA.
 *
 * @Author : Runqiang Jiang
 * @create 2022/6/5 22:57
 */
@Configuration
public class SpringMVCSupport extends WebMvcConfigurationSupport {
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
        registry.addResourceHandler("/css/**").addResourceLocations("/css/");
        registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
        registry.addResourceHandler("/js/**").addResourceLocations("/js/");

    }
}

并且需要在SpringMVCConfig的配置类中扫描一下:

package com.itheima.config;

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

/**
 * Created by IntelliJ IDEA.
 *
 * @Author : Runqiang Jiang
 * @create 2022/5/22 10:13
 */
@Configuration
@ComponentScan({"com.itheima.controller","com.itheima.config"})
@EnableWebMvc
public class SpringMVCConfig {
}
接下来就需要将所有的列表查询、新增、修改、删除等功能一个个来实现下。
4.2 列表功能

 

需求 : 页面加载完后发送异步请求到后台获取列表数据进行展示。
1.找到页面的钩子函数,created()
2. created()方法中调用了this.getAll()方法
3.getAll()方法中使用axios发送异步请求从后台获取数据
4.访问的路径为http://localhost/books
5.返回数据
返回数据 res.data 的内容如下 :

    //列表
                getAll() {
                    //发送Ajax请求
                    axios.get("/books").then((res)=>{
                        this.dataList=res.data.data;
                    })
                },

结果:

4.3 添加功能

需求 : 完成图片的新增功能模块
1.找到页面上的新建按钮,按钮上绑定了@click="handleCreate()"方法
2.method中找到handleCreate方法,方法中打开新增面板
3.新增面板中找到确定按钮,按钮上绑定了@click="handleAdd()"方法
4.method中找到handleAdd方法
5.在方法中发送请求和数据,响应成功后将新增面板关闭并重新查询数据

//弹出添加窗口
                handleCreate() {
                    this.dialogFormVisible=true;
                },

                //重置表单
                resetForm() {
                },

                //添加
                handleAdd () {
                    //发送Ajax请求
                    axios.post("/books",this.formData).then((res)=>{
                        // 如果插入成功,则关闭弹层,显示数据
                        this.dialogFormVisible=false;
                        this.getAll();
                    })
                },
4.4 添加功能状态处理
基础的新增功能已经完成,但是还有一些问题需要解决下 :
需求:新增成功是关闭面板,重新查询数据,那么新增失败以后该如何处理?
1.handlerAdd方法中根据后台返回的数据来进行不同的处理
2.如果后台返回的是成功,则提示成功信息,并关闭面板
3.如果后台返回的是失败,则提示错误信息
(1) 修改前端页面
            //添加
                handleAdd () {
                    //发送Ajax请求
                    axios.post("/books",this.formData).then((res)=>{
                        // 如果插入成功,则关闭弹层,显示数据
                        if(res.data.code==20011){
                            this.dialogFormVisible=false;
                            this.$message.success("添加成功")

                        }else if(res.data.code==20010){
                            this.$message.error("添加失败")
                        }else{
                            this.$message.error(res.data.msg)
                        }

                    }).finally(()=>{
                        this.getAll();
                    })
                },

(2) 后台返回操作结果,将 Dao 层的增删改方法返回值从 void 改成 int
package com.itheima.dao;

import com.itheima.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;

/**
 * Created by IntelliJ IDEA.
 *
 * @Author : Runqiang Jiang
 * @create 2022/5/22 16:14
 */
public interface BookDao {
    // 写法1
    //@Insert("insert into tbl_book values(null,#{type},#{name},#{description})")
    //写法2 第一个type是表的字段名,第二个type.#{type}是book的属性
    @Insert("insert into tbl_book(type,name,description) values(#{type},#{name},#{description})")
    public int save(Book book);
    @Update("update tbl_book set type=#{type},name=#{name},description=#{description} where id=#{id}")
    public int update(Book book);
    @Delete("delete from tbl_book where id=#{id}")
    public int  delete(Integer id);
    @Select("select * from tbl_book where id=#{id}")
    public Book getById(Integer id);
    @Select("select * from tbl_book")
    public List<Book> getAll();
}
(3) BookServiceImpl 中,增删改方法根据 DAO 的返回值来决定返回 true/false
  @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("id") Integer id) {
        boolean delete = bookService.delete(id);
        return new Result(delete ? Code.DELETE_OK : Code.DELETE_ERR, delete);
    }
(4) 测试错误情况,将图书类别长度设置超出范围即可

 处理完新增后,会发现新增还存在一个问题.新增成功后,再次点击新增按钮会发现之前的数据还存在,这个时候就需要在新增的时候将表单内容清空。

     //弹出添加窗口
                handleCreate() {
                    this.dialogFormVisible=true;
                    resetForm();
                },

                //重置表单
                resetForm() {
                    this.formData={};
                },
4.5 修改功能
需求 : 完成图书信息的修改功能
1.找到页面中的编辑按钮,该按钮绑定了@click="handleUpdate(scope.row)"
2.methodhandleUpdate方法中发送异步请求根据ID查询图书信息
3.根据后台返回的结果,判断是否查询成功
如果查询成功打开修改面板回显数据,如果失败提示错误信息
4.修改完成后找到修改面板的确定按钮,该按钮绑定了@click="handleEdit()"
5.在methodhandleEdit方法中发送异步请求提交修改数据
6.根据后台返回的结果,判断是否修改成功
如果成功提示错误信息,关闭修改面板,重新查询数据,如果失败提示错误信息

 scope.row代表的是当前行的行数据,也就是说,scope.row就是选中行对应的json数据,如下:

修改 handleUpdate方法,修改 handleEdit 方法
     //弹出编辑窗口
                handleUpdate(row) {
                    //console.log(row);//row.id
                    //查询数据,根据id查询
                    axios.get("/books"+row.id).then((res)=>{
                        if(res.data.code==20041){
                            this.formData=res.data.data;
                            this.dialogFormVisible4Edit=true;
                        }else{
                            this.$message.error(res.data.msg);

                        }

                    })
                    //展示弹窗,在弹窗中加载数据
                },

                //编辑
                handleEdit() {
                    //发送Ajax请求
                    axios.put("/books",this.formData).then((res)=>{
                        // 如果插入成功,则关闭弹层,显示数据
                        if(res.data.code==20031){
                            this.dialogFormVisible=false;
                            this.$message.success("修改成功")

                        }else if(res.data.code==20030){
                            this.$message.error("修改失败")
                        }else{
                            this.$message.error(res.data.msg)
                        }

                    }).finally(()=>{
                        this.getAll();
                    });
                },

 4.6 删除功能

需求 : 完成页面的删除功能。
1.找到页面的删除按钮,按钮上绑定了@click="handleDelete(scope.row)"
2.methodhandleDelete方法弹出提示框
3.用户点击取消,提示操作已经被取消。
4.用户点击确定,发送异步请求并携带需要删除数据的主键ID
5.根据后台返回结果做不同的操作
如果返回成功,提示成功信息,并重新查询数据
如果返回失败,提示错误信息,并重新查询数据
代码:
    // 删除
                handleDelete(row) {
                    //1.弹出提示框
                    this.$confirm("此操作永久删除当前的数据,是否继续?", "提示", {
                        type: "info"
                    }).then(() => {
                             //2.做删除业务
                        //查询数据,根据id查询
                        axios.delete("/books/" + row.id).then((res) => {
                            // console.log(res.data.data);
                            if (res.data.code == 20021) {
                                //展示弹层,加载数据
                                this.$message.success("删除成功");
                            } else {
                                this.$message.error("删除失败");
                            }
                        });
                    }).catch(() => {
                        //3.取消删除
                        this.$message.info("取消删除操作");
                    }).finally(() => {
                        this.getAll();
                    });

效果:

books.html完整的前端代码:

<!DOCTYPE html>

<html>

    <head>

        <!-- 页面meta -->

        <meta charset="utf-8">

        <meta http-equiv="X-UA-Compatible" content="IE=edge">

        <title>SpringMVC案例</title>

        <meta content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no" name="viewport">

        <!-- 引入样式 -->

        <link rel="stylesheet" href="../plugins/elementui/index.css">

        <link rel="stylesheet" href="../plugins/font-awesome/css/font-awesome.min.css">

        <link rel="stylesheet" href="../css/style.css">

    </head>

    <body class="hold-transition">

        <div id="app">

            <div class="content-header">

                <h1>图书管理</h1>

            </div>

            <div class="app-container">

                <div class="box">

                    <div class="filter-container">

                        <el-input placeholder="图书名称" v-model="pagination.queryString" style="width: 200px;"
                                  class="filter-item"></el-input>

                        <el-button @click="getAll()" class="dalfBut">查询</el-button>

                        <el-button type="primary" class="butT" @click="handleCreate()">新建</el-button>

                    </div>

                    <el-table size="small" current-row-key="id" :data="dataList" stripe highlight-current-row>

                        <el-table-column type="index" align="center" label="序号"></el-table-column>

                        <el-table-column prop="type" label="图书类别" align="center"></el-table-column>

                        <el-table-column prop="name" label="图书名称" align="center"></el-table-column>

                        <el-table-column prop="description" label="描述" align="center"></el-table-column>

                        <el-table-column label="操作" align="center">

                            <template slot-scope="scope">

                                <el-button type="primary" size="mini" @click="handleUpdate(scope.row)">编辑</el-button>

                                <el-button type="danger" size="mini" @click="handleDelete(scope.row)">删除</el-button>

                            </template>

                        </el-table-column>

                    </el-table>

                    <!-- 新增标签弹层 -->

                    <div class="add-form">

                        <el-dialog title="新增图书" :visible.sync="dialogFormVisible">

                            <el-form ref="dataAddForm" :model="formData" :rules="rules" label-position="right"
                                     label-width="100px">

                                <el-row>

                                    <el-col :span="12">

                                        <el-form-item label="图书类别" prop="type">

                                            <el-input v-model="formData.type"/>

                                        </el-form-item>

                                    </el-col>

                                    <el-col :span="12">

                                        <el-form-item label="图书名称" prop="name">

                                            <el-input v-model="formData.name"/>

                                        </el-form-item>

                                    </el-col>

                                </el-row>


                                <el-row>

                                    <el-col :span="24">

                                        <el-form-item label="描述">

                                            <el-input v-model="formData.description" type="textarea"></el-input>

                                        </el-form-item>

                                    </el-col>

                                </el-row>

                            </el-form>

                            <div slot="footer" class="dialog-footer">

                                <el-button @click="dialogFormVisible = false">取消</el-button>

                                <el-button type="primary" @click="handleAdd()">确定</el-button>

                            </div>

                        </el-dialog>

                    </div>

                    <!-- 编辑标签弹层 -->

                    <div class="add-form">

                        <el-dialog title="编辑检查项" :visible.sync="dialogFormVisible4Edit">

                            <el-form ref="dataEditForm" :model="formData" :rules="rules" label-position="right"
                                     label-width="100px">

                                <el-row>

                                    <el-col :span="12">

                                        <el-form-item label="图书类别" prop="type">

                                            <el-input v-model="formData.type"/>

                                        </el-form-item>

                                    </el-col>

                                    <el-col :span="12">

                                        <el-form-item label="图书名称" prop="name">

                                            <el-input v-model="formData.name"/>

                                        </el-form-item>

                                    </el-col>

                                </el-row>

                                <el-row>

                                    <el-col :span="24">

                                        <el-form-item label="描述">

                                            <el-input v-model="formData.description" type="textarea"></el-input>

                                        </el-form-item>

                                    </el-col>

                                </el-row>

                            </el-form>

                            <div slot="footer" class="dialog-footer">

                                <el-button @click="dialogFormVisible4Edit = false">取消</el-button>

                                <el-button type="primary" @click="handleEdit()">确定</el-button>

                            </div>

                        </el-dialog>

                    </div>

                </div>

            </div>

        </div>

    </body>

    <!-- 引入组件库 -->

    <script src="../js/vue.js"></script>

    <script src="../plugins/elementui/index.js"></script>

    <script type="text/javascript" src="../js/jquery.min.js"></script>

    <script src="../js/axios-0.18.0.js"></script>

    <script>
        var vue = new Vue({

            el: '#app',
            data: {
                pagination: {},
                dataList: [],//当前页要展示的列表数据
                formData: {},//表单数据
                dialogFormVisible: false,//控制表单是否可见
                dialogFormVisible4Edit: false,//编辑表单是否可见
                rules: {//校验规则
                    type: [{required: true, message: '图书类别为必填项', trigger: 'blur'}],
                    name: [{required: true, message: '图书名称为必填项', trigger: 'blur'}]
                }
            },

            //钩子函数,VUE对象初始化完成后自动执行
            created() {
                this.getAll();
            },

            methods: {
                //列表
                getAll() {
                    //发送ajax请求
                    axios.get("/books").then((res) => {
                        this.dataList = res.data.data;
                    });
                },

                //弹出添加窗口
                handleCreate() {
                    this.dialogFormVisible = true;
                    this.resetForm();
                },

                //重置表单
                resetForm() {
                    this.formData = {};
                },

                //添加
                handleAdd() {
                    //发送ajax请求
                    axios.post("/books", this.formData).then((res) => {
                        console.log(res.data);
                        //如果操作成功,关闭弹层,显示数据
                        if (res.data.code == 20011) {
                            this.dialogFormVisible = false;
                            this.$message.success("添加成功");
                        } else if (res.data.code == 20010) {
                            this.$message.error("添加失败");
                        } else {
                            this.$message.error(res.data.msg);
                        }
                    }).finally(() => {
                        this.getAll();
                    });
                },

                //弹出编辑窗口
                handleUpdate(row) {
                    // console.log(row);   //row.id 查询条件
                    //查询数据,根据id查询
                    axios.get("/books/" + row.id).then((res) => {
                        // console.log(res.data.data);
                        if (res.data.code == 20041) {
                            //展示弹层,加载数据
                            this.formData = res.data.data;
                            this.dialogFormVisible4Edit = true;
                        } else {
                            this.$message.error(res.data.msg);
                        }
                    });
                },

                //编辑
                handleEdit() {
                    //发送ajax请求
                    axios.put("/books", this.formData).then((res) => {
                        //如果操作成功,关闭弹层,显示数据
                        if (res.data.code == 20031) {
                            this.dialogFormVisible4Edit = false;
                            this.$message.success("修改成功");
                        } else if (res.data.code == 20030) {
                            this.$message.error("修改失败");
                        } else {
                            this.$message.error(res.data.msg);
                        }
                    }).finally(() => {
                        this.getAll();
                    });
                },

                // 删除
                handleDelete(row) {
                    //1.弹出提示框
                    this.$confirm("此操作永久删除当前的数据,是否继续?", "提示", {
                        type: "info"
                    }).then(() => {
                             //2.做删除业务
                        //查询数据,根据id查询
                        axios.delete("/books/" + row.id).then((res) => {
                            // console.log(res.data.data);
                            if (res.data.code == 20021) {
                                //展示弹层,加载数据
                                this.$message.success("删除成功");
                            } else {
                                this.$message.error("删除失败");
                            }
                        });
                    }).catch(() => {
                        //3.取消删除
                        this.$message.info("取消删除操作");
                    }).finally(() => {
                        this.getAll();
                    });



                },
            }
        })

    </script>

</html>

 

5 ,拦截器
对于拦截器这节的知识,需要学习如下内容 :
拦截器概念
拦截器参数
拦截器工作流程分析
5.1 拦截器概念

 

(1)浏览器发送一个请求会先到Tomcatweb服务器
(2)Tomcat服务器接收到请求以后,会去判断请求的是静态资源还是动态资源
(3)如果是静态资源,会直接到Tomcat的项目部署目录下去直接访问
(4)如果是动态资源,就需要交给项目的后台代码进行处理
(5)在找到具体的方法之前,我们可以去配置过滤器(可以配置多个),按照顺序进行执行
(6)然后进入到到中央处理器(SpringMVC中的内容)SpringMVC会根据配置的规则进行拦截
(7)如果满足规则,则进行处理,找到其对应的controller类中的方法进行执行,完成后返回结果
(8)如果不满足规则,则不进行处理
(9)这个时候,如果我们需要在每个Controller方法执行的前后添加业务,具体该如何来实现?
这个就是拦截器要做的事。
拦截器(Interceptor)是一种动态拦截方法调用的机制,在SpringMVC中动态拦截控制器方法
的执行作用: 在指定的方法调用前后执行预先设定的代码 阻止原始方法的执行 总结:拦截器就是用来做增强
拦截器和过滤器在作用和执行顺序上也很相似
所以这个时候,就有一个问题需要思考:拦截器和过滤器之间的区别是什么?
归属不同:Filter属于Servlet技术,Interceptor属于SpringMVC技术
拦截内容不同:Filter对所有访问进行增强,Interceptor仅针对SpringMVC的访问进行增强
拦截器的入门案例:
 ProjectInterceptor 、 SpringMvcSupport
package com.itheima.controller.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;

/**
 * Created by IntelliJ IDEA.
 *
 * @Author : Runqiang Jiang
 * @create 2022/6/12 0:13
 */
@Component
public class ProjectInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandler");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandler");

        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterHandler");
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}
package com.itheima.config;

import com.itheima.controller.interceptor.ProjectInterceptor;
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;

/**
 * Created by IntelliJ IDEA.
 *
 * @Author : Runqiang Jiang
 * @create 2022/6/12 0:17
 */
@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/*");
    }
}
拦截器的执行流程 :
5.3 拦截器参数
5.3.1 前置处理方法
原始方法之前运行 preHandle
  @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandler");
        return true;
    }
request: 请求对象
response: 响应对象
handler: 被调用的处理器对象,本质上是一个方法对象,对反射中的 Method 对象进行了再包装
使用request对象可以获取请求数据中的内容,如获取请求头的Content-Type
使用handler参数,可以获取方法的相关信息
  @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandler");
        String header = request.getHeader("Content-type");
        System.out.println("preHandler: "+header);
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        String name = handlerMethod.getMethod().getName();//方法名称
        System.out.println("prehandler:"+name);
        return true;
    }

 后台运行结果:

 

5.3.2 后置处理方法
原始方法运行后运行,如果原始方法被拦截,则不执行
 @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandler");

        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }
前三个参数和上面的是一致的。
modelAndView:如果处理器执行完成具有返回结果,可以读取到对应数据与页面信息,并进行调整 现在都是返回json数据,所以该参数的使用率不高。
5.3.3 完成处理方法
拦截器最后执行的方法,无论原始方法是否执行
  @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterHandler");
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
前三个参数与上面的是一致的。
ex: 如果处理器执行过程中出现异常对象,可以针对异常情况进行单独处理
因为现在已经有全局异常处理器类,所以该参数的使用率也不高。
这三个方法中,最常用的是 preHandle , 在这个方法中可以通过返回值来决定是否要进行放行,可以把业务逻辑放在该方法中,如果满足业务则返回true 放行,不满足则返回 false 拦截。
5.4 拦截器链配置
目前在项目中只添加了一个拦截器,如果有多个,该如何配置 ? 配置多个后,执行顺序是什么 ?
5.4.1 配置多个拦截器
package com.itheima.controller.interceptor;

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

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

@Component
public class ProjectInterceptor2 implements HandlerInterceptor {
    @Override
    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...222");
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle...222");
    }

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

配置拦截器类:

package com.itheima.config;

import com.itheima.controller.interceptor.ProjectIntercepto;
import com.itheima.controller.interceptor.ProjectInterceptor2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

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

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //配置多拦截器
        registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*");
        registry.addInterceptor(projectInterceptor2).addPathPatterns("/books","/books/*");
    }
}
拦截器执行的顺序是和配置顺序有关。就和前面所提到的运维人员进入机房的案例,先进后出。
当配置多个拦截器时,形成拦截器链
拦截器链的运行顺序参照拦截器添加顺序为准
当拦截器中出现对原始处理器的拦截,后面的拦截器均终止运行
当拦截器运行中断,仅运行配置在前面的拦截器的afterCompletion操作

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值