当前位置: 首页 > news >正文

SpringMVC——SSM整合

SSM整合

创建工程

在pom.xml中导入坐标

<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/maven-v4_0_0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.ling</groupId><artifactId>springmvc_09_result</artifactId><packaging>war</packaging><version>1.0-SNAPSHOT</version><dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>6.0.6</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>6.0.6</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>6.0.6</version></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.16</version></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>3.0.1</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.30</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.15</version></dependency><dependency><groupId>jakarta.servlet</groupId><artifactId>jakarta.servlet-api</artifactId><version>6.1.0</version><scope>provided</scope></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.14.2</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version><scope>test</scope></dependency></dependencies>
</project>

创建好的工程结构如下:

SSM整合

Spring整合MyBatis

创建数据库和表
-- 创建ssm_db数据库
CREATE DATABASE IF NOT EXISTS ssm_db CHARACTER SET utf8;-- 使用ssm_db数据库
USE ssm_db;-- 创建tbl_book表
CREATE TABLE tbl_book(id INT PRIMARY KEY AUTO_INCREMENT, -- 图书编号TYPE VARCHAR(100), -- 图书类型NAME VARCHAR(100), -- 图书名称description VARCHAR(100) -- 图书描述
);
-- 添加初始化数据
INSERT INTO tbl_book VALUES(NULL,'计算机理论','Spring实战 第5版','Spring入门经典教材,深入理解Spring原理技术内幕');
INSERT INTO tbl_book VALUES(NULL,'计算机理论','Spring 5核心原理与30个类手写实战','十年沉淀之作,手写Spring精华思想');
INSERT INTO tbl_book VALUES(NULL,'计算机理论','Spring 5设计模式','深入Spring源码剖析,Spring源码蕴含的10大设计模式');
INSERT INTO tbl_book VALUES(NULL,'市场营销','直播就该这么做:主播高效沟通实战指南','李子柒、李佳琦、薇娅成长为网红的秘密都在书中');
INSERT INTO tbl_book VALUES(NULL,'市场营销','直播销讲实战一本通','和秋叶一起学系列网络营销书籍');
INSERT INTO tbl_book VALUES(NULL,'市场营销','直播带货:淘宝、天猫直播从新手到高手','一本教你如何玩转直播的书,10堂课轻松实现带货月入3W+');
jdbc.properties属性文件
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm_db
jdbc.username=root
jdbc.password=123456
JdbcConfig配置类
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;@Beanpublic DataSource DataSource() {DruidDataSource dataSource = new DruidDataSource();dataSource.setDriverClassName(driver);dataSource.setUrl(url);dataSource.setUsername(username);dataSource.setPassword(password);return dataSource;}@Beanpublic PlatformTransactionManager transactionManager(DataSource dataSource) {DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();transactionManager.setDataSource(dataSource);return transactionManager;}
}
MyBatisConfig配置类
package com.ling.config;import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.Bean;import javax.sql.DataSource;public class MyBatisConfig {@Beanpublic SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) {SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();factoryBean.setDataSource(dataSource);factoryBean.setTypeAliasesPackage("com.ling,pojo");return factoryBean;}@Beanpublic MapperScannerConfigurer mapperScannerConfigurer() {MapperScannerConfigurer msc = new MapperScannerConfigurer();msc.setBasePackage("com.ling.dao");return msc;}
}
SpringConfig配置类
@Configuration
@ComponentScan({"com.ling.service", "com.ling.dao"})
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class, MyBatisConfig.class})
@EnableTransactionManagement
public class SpringConfig {// 这里可以添加额外的 Bean 定义或者配置方法
}

Spring整合SpringMVC

SpringMvcConfig配置类
@Configuration
@ComponentScan("com.ling.controller")
@EnableWebMvc
public class SpringMvcConfig {
}
ServletConfig配置类,加载SpringMvcConfig和SpringConfig配置类
public class ServletConfig extends AbstractAnnotationConfigDispatcherServletInitializer {@Overrideprotected Class<?>[] getRootConfigClasses() {return new Class[]{SpringConfig.class};//Spring的配置类}@Overrideprotected Class<?>[] getServletConfigClasses() {return new Class[]{SpringMvcConfig.class};//SpringMVC的配置类}@Overrideprotected String[] getServletMappings() {return new String[]{"/"};//映射路径}
}

功能模块开发

数据层开发(BookDao)
Book实体类
public class Book {private Integer id;private String type;private String name;private String description;//同学们自己添加getter、setter、toString()方法
}
BookDao接口
package com.ling.dao;import com.ling.pojo.Book;
import org.apache.ibatis.annotations.*;import java.util.List;
public interface BookDao {@Insert("INSERT INTO tbl_book(type, name, description) VALUES (#{type}, #{name}, #{description})")void save(Book book);@Update("UPDATE tbl_book SET type = #{type}, name = #{name}, description = #{description} WHERE id = #{id}")void update(Book book);@Delete("DELETE FROM tbl_book WHERE id = #{id}")void delete(Integer id);@Select("SELECT * FROM tbl_book WHERE id = #{id}")Book getById(Integer id);@Select("SELECT * FROM tbl_book")List<Book> getAll();
}
业务层开发(BookService/BookServiceImpl)
BookService接口
@Transactional
public interface BookService {/*** 保存书籍信息* @param book 书籍对象* @return 保存成功返回true,否则返回false*/boolean save(Book book);/*** 更新书籍信息* @param book 书籍对象* @return 更新成功返回true,否则返回false*/boolean update(Book book);/*** 根据ID删除书籍* @param id 书籍ID* @return 删除成功返回true,否则返回false*/boolean delete(Integer id);/*** 根据ID查询书籍* @param id 书籍ID* @return 查询到的书籍对象*/Book getById(Integer id);/*** 查询所有书籍* @return 书籍列表*/List<Book> getAll();
}
BookServiceImpl实现类
@Service // 标记这是一个服务组件
public class BookServiceImpl implements BookService {@Autowired // 自动注入BookDaoprivate BookDao bookDao;@Override@Transactional // 标记这个方法为事务方法public boolean save(Book book) {bookDao.save(book);return true;}@Override@Transactionalpublic boolean update(Book book) {bookDao.update(book);return true;}@Override@Transactionalpublic boolean delete(Integer id) {bookDao.delete(id);return true;}@Overridepublic Book getById(Integer id) {return bookDao.getById(id);}@Overridepublic List<Book> getAll() {return bookDao.getAll();}}
表现层开发(BookController)
@RestController
@RequestMapping("/books")
public class BookController {@Autowiredprivate BookService bookService;@PostMappingpublic boolean save(@RequestBody Book book){return bookService.save(book);}@PutMappingpublic boolean update(@RequestBody Book book){return bookService.update(book);}@DeleteMapping("/{id}")public boolean delete(@PathVariable Integer id){return bookService.delete(id);}@GetMapping("/{id}")public Book getById(@PathVariable Integer id){return bookService.getById(id);}@GetMappingpublic List<Book> getAll(){return bookService.getAll();}}

接口测试

Spring整合Junit测试业务层方法
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class BookServiceTest {@Autowiredprivate BookService bookService;@Testpublic void testGetById(){Book book = bookService.getById(1);System.out.println(book);}@Testpublic void testGetAll(){List<Book> books = bookService.getAll();System.out.println(books);}
}

Postman测试表现层接口

测试保存图书

测试修改图书

测试删除图书

测试查询单本图书

测试查询全部图书

表现层数据封装

问题导入
  • 目前我们表现层响应给客户端的数据有哪几种?

表现层响应数据的问题

  • 问题:我们表现层增删改方法返回true或者false表示是否成功,getById()方法返回一个json对象,getAll()方法返回一个json对象数组,这里就出现了三种格式的响应结果,极其不利于前端解析。

  • 解决:我们需要统一响应结果的格式

定义Result类封装响应结果

Result类封装响应结果
public class Result {//描述统一格式中的编码,用于区分操作,可以简化配置0或1表示成功失败private Integer code;//描述统一格式中的数据private Object data;//描述统一格式中的消息,可选属性private String msg;public Result() {}public Result(Integer code,Object data) {this.code = code;this.data = data;}public Result(Integer code, Object data, String msg) {this.code = code;this.data = data;this.msg = msg;}//同学们自己添加getter、setter、toString()方法
}

注意事项:

  • Result类中的字段并不是固定的,可以根据需要自行增减

Code类封装响应码
public class Code {public static final Integer SAVE_OK = 20011;public static final Integer DELETE_OK = 20021;public static final Integer UPDATE_OK = 20031;public static final Integer GET_OK = 20041;public static final Integer SAVE_ERROR = 20010;public static final Integer DELETE_ERROR = 20020;public static final Integer UPDATE_ERROR = 20030;public static final Integer GET_ERROR = 20040;
}

注意事项:

  • Code类的常量设计也不是固定的,可以根据需要自行增减,例如将查询再进行细分为GET_OK,GET_ALL_OK,GET_PAGE_OK

表现层数据封装返回Result对象
@RestController
@RequestMapping("/books")
public class BookController {@Autowiredprivate BookService bookService;@PostMappingpublic Result save(@RequestBody Book book){boolean flag = bookService.save(book);return new Result(flag ? Code.SAVE_OK : Code.SAVE_ERROR, flag);}@PutMappingpublic Result update(@RequestBody Book book){boolean flag = bookService.update(book);return new Result(flag ? Code.UPDATE_OK : Code.UPDATE_ERROR, flag);}@DeleteMapping("/{id}")public Result delete(@PathVariable Integer id){boolean flag = bookService.delete(id);return new Result(flag ? Code.DELETE_OK : Code.DELETE_ERROR, flag);}@GetMapping("/{id}")public Result getById(@PathVariable Integer id){Book book = bookService.getById(id);Integer code = book != null ? Code.GET_OK : Code.GET_ERROR;String msg = book != null ? "" : "数据查询失败,请重试";return new Result(code, book, msg);}@GetMappingpublic Result getAll(){List<Book> books = bookService.getAll();Integer code = books != null ? Code.GET_OK : Code.GET_ERROR;String msg = books != null ? "" : "数据查询失败,请重试";return new Result(code, books, msg);}
}

异常处理器

问题导入
  • 问题1:项目各个个层级均可能出现异常,异常处理代码书写在哪一层?

异常介绍

  • 程序开发过程中不可避免的会遇到异常现象,我们不能让用户看到这样的页面数据

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

编写异常处理器

@RestControllerAdvice  //用于标识当前类为REST风格对应的异常处理器
public class ProjectExceptionAdvice {//统一处理所有的Exception异常@ExceptionHandler(Exception.class)public Result doException(Exception e) {System.out.println("异常往哪里跑");return new Result(666, null, "异常往哪里跑");}
}
使用异常处理器之后的效果

知识点

@RestControllerAdvice注解介绍
  • 名称:@RestControllerAdvice

  • 类型:类注解

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

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

  • 说明:此注解自带@ResponseBody注解与@Component注解,具备对应的功能

@ExceptionHandler注解介绍
名称:@ExceptionHandler
  • 类型:方法注解
  • 位置:专用于异常处理的控制器方法上方
  • 作用:设置指定异常的处理方案,功能等同于控制器方法,出现异常后终止原始控制器执行,并转入当前方法执行
  • 说明:此类方法可以根据处理的异常不同,制作多个方法分别处理对应的异常

项目异常处理方案

项目异常分类

  • 业务异常(BusinessException)
    • 项目运行过程中可预计且无法避免的异常

  • 系统异常(SystemException)
    • 项目运行过程中可预计且无法避免的异常

  • 其他异常(Exception)

    • 编程人员未预期到的异常

项目异常处理方案

  • 业务异常(BusinessException)

    • 发送对应消息传递给用户,提醒规范操作

  • 系统异常(SystemException)

    • 发送固定消息传递给用户,安抚用户

    • 发送特定消息给运维人员,提醒维护

    • 记录日志

  • 其他异常(Exception)

    • 发送固定消息传递给用户,安抚用户

    • 发送特定消息给编程人员,提醒维护(纳入预期范围内)

    • 记录日志

项目异常处理代码实现

根据异常分类自定义异常类
自定义项目系统级异常
//自定义异常处理器,用于封装异常信息,对异常进行分类
public class SystemException extends RuntimeException{private Integer code;public Integer getCode() {return code;}public void setCode(Integer code) {this.code = code;}public SystemException(Integer code, String message) {super(message);this.code = code;}public SystemException(Integer code, String message, Throwable cause) {super(message, cause);this.code = code;}
}
自定义项目业务级异常
//自定义异常处理器,用于封装异常信息,对异常进行分类
public class BusinessException extends RuntimeException{private Integer code;public Integer getCode() {return code;}public void setCode(Integer code) {this.code = code;}public BusinessException(Integer code, String message) {super(message);this.code = code;}public BusinessException(Integer code,String message,Throwable cause) {super(message, cause);this.code = code;}
}
自定义异常编码(持续补充)
public class Code {//之前其他状态码省略没写,以下是新补充的状态码,可以根据需要自己补充public static final Integer SYSTEM_ERR = 50001;public static final Integer SYSTEM_TIMEOUT_ERR = 50002;public static final Integer SYSTEM_UNKNOW_ERR = 59999;public static final Integer BUSINESS_ERR = 60002;}
触发自定义异常
@Service
public class BookServiceImpl implements BookService {@Autowiredprivate BookDao bookDao;//在getById演示触发异常,其他方法省略没有写进来@Overridepublic Book getById(Integer id) {if (id < 0){throw new BusinessException(Code.BUSINESS_ERR, "请不要使用你的技术挑战我的耐心");}// 将可能出现的异常进行包装,转换成自定义异常try {int i = 1/0;}catch (Exception e){throw new SystemException(Code.SYSTEM_TIMEOUT_ERR, "服务器访问超时,请重试", e);}return bookDao.getById(id);}
}
在异常通知类中拦截并处理异常
@RestControllerAdvice //用于标识当前类为REST风格对应的异常处理器
public class ProjectExceptionAdvice {//@ExceptionHandler用于设置当前处理器类对应的异常类型@ExceptionHandler(SystemException.class)public Result doSystemException(SystemException ex){//记录日志//发送消息给运维//发送邮件给开发人员,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());}//除了自定义的异常处理器,保留对Exception类型的异常处理,用于处理非预期的异常@ExceptionHandler(Exception.class)public Result doOtherException(Exception ex){//记录日志//发送消息给运维//发送邮件给开发人员,ex对象发送给开发人员return new Result(Code.SYSTEM_UNKNOW_ERR,null,"系统繁忙,请稍后再试!");}
}
测试:在postman中发送请求访问getById方法,传递参数-1,得到以下结果:

SSM整合页面开发

准备工作

  • 为了确保静态资源能够被访问到,需要设置静态资源过滤
@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {@Overrideprotected void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/css/**").addResourceLocations("/css/");registry.addResourceHandler("/js/**").addResourceLocations("/js/");registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");}
}
列表查询功能
  • 前端代码

//列表
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();});
}
  • 后台代码改进

@Service
public class BookServiceImpl implements BookService {@Autowiredprivate BookDao bookDao;//增删改的方法判断了影响的行数是否大于0,而不是固定返回truepublic boolean save(Book book) {return bookDao.save(book) > 0;}//增删改的方法判断了影响的行数是否大于0,而不是固定返回truepublic boolean update(Book book) {return bookDao.update(book) > 0;}//增删改的方法判断了影响的行数是否大于0,而不是固定返回truepublic boolean delete(Integer id) {return bookDao.delete(id) > 0;}public Book getById(Integer id) {if(id < 0){throw new BusinessException(Code.BUSINESS_ERR,"请不要使用你的技术挑战我的耐性!");return bookDao.getById(id);}}public List<Book> getAll() {return bookDao.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.做删除业务axios.delete("/books/"+row.id).then((res)=>{if(res.data.code == 20021){this.$message.success("删除成功");}else{this.$message.error("删除失败");}}).finally(()=>{this.getAll();});}).catch(()=>{//3.取消删除this.$message.info("取消删除操作");});
}

拦截器

拦截器简介

问题导入
  • 问题1:拦截器拦截的对象是谁?
  • 问题2:拦截器和过滤器有什么区别?
拦截器概念和作用

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

  • 作用:

    1. 在指定的方法调用前后执行预先设定的代码

    2. 阻止原始方法的执行

    3. 总结:增强

  • 核心原理:AOP思想

拦截器和过滤器的区别
  • 归属不同:Filter属于Servlet技术,Interceptor属于SpringMVC技术

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

入门案例

问题导入
  • 定义拦截器需要实现什么接口?
拦截器代码实现

【第一步】定义拦截器

做法:定义一个类,实现HandlerInterceptor接口即可

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

【第二步】配置加载拦截器

@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {@Autowiredprivate ProjectInterceptor projectInterceptor;@Overrideprotected void addInterceptors(InterceptorRegistry registry) {//配置拦截器registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*");}
}

使用标准接口WebMvcConfigurer简化开发(注意:侵入式较强)

@Configuration
@ComponentScan({"com.itheima.controller"})
@EnableWebMvc
//实现WebMvcConfigurer接口可以简化开发,但具有一定的侵入性
public class SpringMvcConfig implements WebMvcConfigurer {@Autowiredprivate ProjectInterceptor projectInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {//配置多拦截器registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*");}
}
拦截器流程分析

拦截器参数
问题导入
  • postHandle()和afterCompletion()方法都是处理器方法执行之后执行,有什么区别?
前置处理
//原始方法调用前执行的内容
//返回值类型可以拦截控制的执行,true放行,false终止
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("preHandle..."+contentType);return true;
}
  • 参数

    1. request:请求对象

    2. response:响应对象

    3. handler:被调用的处理器对象,本质上是一个方法对象,对反射技术中的Method对象进行了再包装

  • 返回值 返回值为false,被拦截的处理器将不执行。

后置处理
//原始方法调用后执行的内容
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("postHandle...");
}
  • 参数 modelAndView:如果处理器执行完成具有返回结果,可以读取到对应数据与页面信息,并进行跳转

  • 注意:如果处理器方法出现异常了,该方法不会执行
完成后处理
//原始方法调用完成后执行的内容
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("afterCompletion...");
}
  • 参数 ex:如果处理器执行过程中出现异常对象,可以针对异常情况进行单独处理

  • 注意:无论处理器方法内部是否出现异常,该方法都会执行。

拦截器链配置

问题导入
  • 什么是拦截器链?
多个拦截器配置
  • 定义第二个拦截器

@Component
public class ProjectInterceptor2 implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("preHandle...222");return false;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("postHandle...222");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("afterCompletion...222");}
}
  • 配置第二个拦截器

@Configuration
@ComponentScan({"com.itheima.controller"})
@EnableWebMvc
//实现WebMvcConfigurer接口可以简化开发,但具有一定的侵入性
public class SpringMvcConfig implements WebMvcConfigurer {@Autowiredprivate ProjectInterceptor projectInterceptor;@Autowiredprivate ProjectInterceptor2 projectInterceptor2;@Overridepublic void addInterceptors(InterceptorRegistry registry) {//配置多拦截器registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*");registry.addInterceptor(projectInterceptor2).addPathPatterns("/books","/books/*");}
}
多个连接器工作流程分析
  • 当配置多个拦截器时,形成拦截器链

  • 拦截器链的运行顺序参照拦截器添加顺序为准

  • 当拦截器中出现对原始处理器的拦截,后面的拦截器均终止运行

  • 当拦截器运行中断,仅运行配置在前面的拦截器的afterCompletion操作

​​​​​​​

相关文章:

SpringMVC——SSM整合

SSM整合 创建工程 在pom.xml中导入坐标 <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/maven-v4_…...

Windows系统电脑安装TightVNC服务端结合内网穿透实现异地远程桌面

文章目录 前言1. 安装TightVNC服务端2. 局域网VNC远程测试3. Win安装Cpolar工具4. 配置VNC远程地址5. VNC远程桌面连接6. 固定VNC远程地址7. 固定VNC地址测试 前言 在追求高效、便捷的数字化办公与生活的今天&#xff0c;远程桌面服务成为了连接不同地点、不同设备之间的重要桥…...

【ubuntu24.04】GTX4700 配置安装cuda

筛选显卡驱动显卡驱动 NVIDIA-Linux-x86_64-550.135.run 而后重启:最新的是12.6 用于ubuntu24.04 ,但是我的4700的显卡驱动要求12.4 cuda...

Spring Boot 动态数据源切换

背景 随着互联网应用的快速发展&#xff0c;多数据源的需求日益增多。Spring Boot 以其简洁的配置和强大的功能&#xff0c;成为实现动态数据源切换的理想选择。本文将通过具体的配置和代码示例&#xff0c;详细介绍如何在 Spring Boot 应用中实现动态数据源切换&#xff0c;帮…...

MySQL技巧之跨服务器数据查询:进阶篇-从A服务器的MySQ数据库复制到B服务器的SQL Server数据库的表中

MySQL技巧之跨服务器数据查询&#xff1a;进阶篇-从A服务器的MySQ数据库复制到B服务器的SQL Server数据库的表中 基础篇已经描述&#xff1a;借用微软的SQL Server ODBC 即可实现MySQL跨服务器间的数据查询。 而且还介绍了如何获得一个在MS SQL Server 可以连接指定实例的MyS…...

大语言模型LLM的微调中 QA 转换的小工具 xlsx2json.py

在训练语言模型中&#xff0c;需要将文件整理成规范的文档&#xff0c;因为文档本身会有很多不规范的地方&#xff0c;为了训练的正确&#xff0c;将文档进行规范处理。代码的功能是读取一个 Excel 文件&#xff0c;将其数据转换为 JSON 格式&#xff0c;并将 JSON 数据写入到一…...

CFD 在生物反应器放大过程中的作用

工艺工程师最常想到的一个问题是“如何将台式反应器扩大到工业规模的反应器&#xff1f;”。这个问题的答案并不简单&#xff0c;也不容易得到。例如&#xff0c;人们误以为工业规模的反应器的性能与台式反应器相同。因此&#xff0c;扩大规模的过程并不是一件容易的事。必须对…...

Axios与FastAPI结合:构建并请求用户增删改查接口

在现代Web开发中&#xff0c;FastAPI以其高性能和简洁的代码结构成为了构建RESTful API的热门选择。而Axios则因其基于Promise的HTTP客户端特性&#xff0c;成为了前端与后端交互的理想工具。本文将介绍FastAPI和Axios的结合使用&#xff0c;通过一个用户增删改查&#xff08;C…...

美畅物联丨如何通过ffmpeg排查视频问题

在我们日常使用畅联AIoT开放云平台的过程中&#xff0c;摄像机视频无法播放是较为常见的故障。尤其是当碰到摄像机视频不能正常播放的状况时&#xff0c;哪怕重启摄像机&#xff0c;也仍然无法使其恢复正常的工作状态&#xff0c;这着实让人感到头疼。这个时候&#xff0c;可以…...

基于OpenCV视觉库让机械手根据视觉判断物体有无和分类抓取的例程

项目实例&#xff0c;在一个无人封闭的隔绝场景中&#xff0c;根据视觉判断物件的有无&#xff0c;通过机械手 进行物件分类提取&#xff0c;并且返回状态结果&#xff1b; 实际的场景是有一个类似采血的固件支架盘&#xff0c;上面很多采血管&#xff0c;采血管帽颜色可能不同…...

QChart数据可视化

目录 一、QChart基本介绍 1.1 QChart基本概念与用途 1.2 主要类的介绍 1.2.1 QChartView类 1.2.2 QChart类 1.2.3QAbstractSeries类 1.2.4 QAbstractAxis类 1.2.5 QLegendMarker 二、与图表交互 1. 动态绘制数据 2. 深入数据 3. 缩放和滚动 4. 鼠标悬停 三、主题 …...

转换的艺术:如何在JavaScript中序列化Set为Array、Object及逆向操作

先认识一下Set 概念&#xff1a;存储唯一值的集合&#xff0c;元素只能是值&#xff0c;没有键与之对应。Set中的每个值都是唯一的。 特性&#xff1a; 值的集合&#xff0c;值可以是任何类型。 值的唯一性&#xff0c;每个值只能出现一次。 保持了插入顺序。 不支持通过索引来…...

万能门店小程序管理系统存在前台任意文件上传漏洞

免责声明: 本文旨在提供有关特定漏洞的深入信息,帮助用户充分了解潜在的安全风险。发布此信息的目的在于提升网络安全意识和推动技术进步,未经授权访问系统、网络或应用程序,可能会导致法律责任或严重后果。因此,作者不对读者基于本文内容所采取的任何行为承担责任。读者在…...

详解Rust泛型用法

文章目录 基础语法泛型与结构体泛型约束泛型与生命周期泛型与枚举泛型和Vec静态泛型(const 泛型)类型别名默认类型参数Sized Trait与泛型常量函数与泛型泛型的性能 Rust是一种系统编程语言&#xff0c;它拥有强大的泛型支持&#xff0c;泛型是Rust中用于实现代码复用和类型安全…...

移远通信携手紫光展锐,以“5G+算力”共绘万物智联新蓝图

11月26日&#xff0c;2024紫光展锐全球合作伙伴大会在上海举办。作为紫光展锐重要的合作伙伴&#xff0c;移远通信应邀参会。 在下午的物联网生态论坛上&#xff0c;移远通信产品总监胡勇华作题为“5G与算力双擎驱动 引领智联新未来”的演讲&#xff0c;深度剖析了产业发展的趋…...

Mybatis:Mybatis快速入门

Mybatis的官方文档是真的非常好&#xff01;非常好&#xff01; 点一下我呗&#xff1a;Mybatis官方文档 MyBatis 是一款优秀的持久层框架&#xff0c;它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可…...

微信小程序用户登录页面制作教程

微信小程序用户登录页面制作教程 前言 在微信小程序的开发过程中,用户登录是一个至关重要的功能。通过用户登录,我们可以为用户提供个性化的体验,保护用户数据,并实现更复杂的业务逻辑。本文将为您详细讲解如何制作一个用户登录页面,包括设计思路、代码示例以及实现细节…...

python+django自动化平台(一键执行sql) 前端vue-element展示

一、开发环境搭建和配置 pip install mysql-connector-pythonpip install PyMySQL二、django模块目录 dbOperations ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-313.pyc │ ├── admin.cpython-313.pyc │ ├── apps.cpython-313.pyc │ …...

JavaScript学习总结

前言 JavaScript的学习花的时间比较长&#xff0c;如何进行正确的学习&#xff1f;今天进行总结与整理。 首先&#xff0c;明确JavaScript是什么&#xff1f;它的结构框架是什么&#xff0c;有哪些操作与组成部分。 其次&#xff0c;通过案例实践&#xff0c;清楚达到什么效果…...

Python 3 教程第22篇(数据结构)

Python3 数据结构 本章节我们主要结合前面所学的知识点来介绍Python数据结构。 列表 Python中列表是可变的&#xff0c;这是它区别于字符串和元组的最重要的特点&#xff0c;一句话概括即&#xff1a;列表可以修改&#xff0c;而字符串和元组不能。 以下是 Python 中列表的方…...

Chapter03-Authentication vulnerabilities

文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...

业务系统对接大模型的基础方案:架构设计与关键步骤

业务系统对接大模型&#xff1a;架构设计与关键步骤 在当今数字化转型的浪潮中&#xff0c;大语言模型&#xff08;LLM&#xff09;已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中&#xff0c;不仅可以优化用户体验&#xff0c;还能为业务决策提供…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂

蛋白质结合剂&#xff08;如抗体、抑制肽&#xff09;在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上&#xff0c;高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术&#xff0c;但这类方法普遍面临资源消耗巨大、研发周期冗长…...

oracle与MySQL数据库之间数据同步的技术要点

Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异&#xff0c;它们的数据同步要求既要保持数据的准确性和一致性&#xff0c;又要处理好性能问题。以下是一些主要的技术要点&#xff1a; 数据结构差异 数据类型差异&#xff…...

Ascend NPU上适配Step-Audio模型

1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统&#xff0c;支持多语言对话&#xff08;如 中文&#xff0c;英文&#xff0c;日语&#xff09;&#xff0c;语音情感&#xff08;如 开心&#xff0c;悲伤&#xff09;&#x…...

CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云

目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)

名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...

Python 实现 Web 静态服务器(HTTP 协议)

目录 一、在本地启动 HTTP 服务器1. Windows 下安装 node.js1&#xff09;下载安装包2&#xff09;配置环境变量3&#xff09;安装镜像4&#xff09;node.js 的常用命令 2. 安装 http-server 服务3. 使用 http-server 开启服务1&#xff09;使用 http-server2&#xff09;详解 …...

【p2p、分布式,区块链笔记 MESH】Bluetooth蓝牙通信 BLE Mesh协议的拓扑结构 定向转发机制

目录 节点的功能承载层&#xff08;GATT/Adv&#xff09;局限性&#xff1a; 拓扑关系定向转发机制定向转发意义 CG 节点的功能 节点的功能由节点支持的特性和功能决定。所有节点都能够发送和接收网格消息。节点还可以选择支持一个或多个附加功能&#xff0c;如 Configuration …...