springMVC02-SSM整合(Result统一响应数据格式、异常页面修改、SSM整合vue-elementUI小案例、SpringMVC的拦截器Interceptor)
文章目录
- 今日内容
- 一、SSM整合【重点】
- 1 SSM整合配置
- 问题导入
- 1.1 SSM整合流程
- 1.2 SSM整合配置
- 1.2.1 创建工程,添加依赖和插件
- 1.2.2 Spring整合Mybatis
- 1.2.3 Spring整合SpringMVC
- 2 功能模块开发
- 2.1 数据层开发(BookDao)
- 2.2 业务层开发(BookService/BookServiceImpl)
- 2.3 表现层开发(BookController)
- 3 接口测试
- 3.1 Spring整合Junit测试业务层方法 ==(开发过程中Service开发完毕要停下来用Junit进行业务层接口测试)==
- 3.2 postman测试表现层接口 ==(开发过程中表现层开发完毕要停下来用PostMan进行表现层测试)==
- 二、表现层数据封装【重点】
- 问题导入
- 1 表现层响应数据的问题
- 2 定义Result类封装响应结果
- 2.1 Result类封装响应结果
- 2.2 Code类封装响应码
- 3 表现层数据封装返回Result对象
- 三、异常处理器【理解】
- 问题导入
- 1 异常介绍
- 2 异常处理器
- 2.2.1 编写异常处理器
- 2.2.2 @RestControllerAdvice注解介绍
- 2.2.3 @ExceptionHandler注解介绍
- 四、项目异常处理方案【理解】
- 问题导入
- 1 项目异常分类
- 2 项目异常处理方案
- 3 项目异常处理代码实现
- 3.1 根据异常分类自定义异常类
- 3.1.1 自定义项目系统级异常
- 3.1.2 自定义项目业务级异常
- 3.2 自定义异常编码(持续补充)
- 3.3 触发自定义异常
- 3.4 在异常通知类中拦截并处理异常
- 五、SSM整合页面开发【重点】
- 1 准备工作
- 2 列表查询功能
- 3 添加功能
- 4 修改功能
- 5 删除功能
- 六、拦截器【理解】
- 1 拦截器简介
- 问题导入
- 1.1 拦截器概念和作用
- 1.2 拦截器和过滤器的区别
- 2 入门案例
- 问题导入
- 2.0 环境准备
- 2.1 拦截器代码实现
- 【第一步】定义拦截器
- 【第二步】配置加载拦截器
- 2.2 拦截器流程分析
- 3 拦截器参数
- 问题导入
- 3.1 前置处理(▲ 实用性最强)
- 3.2 后置处理
- 3.3 完成后处理
- 4 拦截器链配置
- 问题导入
- 4.1 多个拦截器配置
- 4.2 多个连接器工作流程分析
今日内容
- 能够掌握SSM整合的流程
- 能够编写SSM整合功能模块类
- 能够使用Result统一表现层响应结果
- 能够编写异常处理器进行项目异常
- 能够完成SSM整合前端页面发送请求实现增删改查操作
- 能够编写拦截器并配置拦截器
一、SSM整合【重点】
1 SSM整合配置
问题导入
请描述“SSM整合流程”中各个配置类的作用?
1.1 SSM整合流程
- 创建工程
- SSM整合
- Spring
- SpringConfig
- MyBatis
- MybatisConfig
- JdbcConfig
- jdbc.properties
- SpringMVC
- ServletContainersInitConfig
- SpringMvcConfig
- Spring
- 功能模块
- 表与实体类
- dao(接口+自动代理)
- service(接口+实现类)
- 业务层接口测试(整合JUnit)
- controller
- 表现层接口测试(PostMan)
1.2 SSM整合配置
1.2.1 创建工程,添加依赖和插件
补全一下目录结构:
最终目录结构先贴出来:
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-jdbc</artifactId><version>5.2.10.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.2.10.RELEASE</version></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.6</version></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>1.3.0</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.47</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.16</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><scope>provided</scope></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.9.0</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.26</version></dependency>
</dependencies><build><plugins><plugin><groupId>org.apache.tomcat.maven</groupId><artifactId>tomcat7-maven-plugin</artifactId><version>2.1</version><configuration><port>80</port><path>/</path></configuration></plugin></plugins>
</build>
下面依次创建各个文件
1.2.2 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+');
其实这些文件,可以直接创建模板文件,直接生成
对应包下右键new
- jdbc.properties属性文件 (resources根目录下)
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/ssm_db?useSSL=false
jdbc.username=root
jdbc.password=1234
- JdbcConfig配置类
package cn.whu.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 org.springframework.transaction.PlatformTransactionManager;import javax.sql.DataSource;public class JdbcConfig {//这里可以加载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;//1.定义一个方法获得要管理的对象//2.添加@Bean,表示当前方法的返回值是一个bean@Beanpublic DataSource dataSource() {DruidDataSource ds = new DruidDataSource();ds.setDriverClassName(driver);ds.setUrl(url);ds.setUsername(username);ds.setPassword(password);return ds;}//Spring事务管理需要的平台事务管理器对象@Beanpublic PlatformTransactionManager transactionManager(DataSource dataSource){DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();transactionManager.setDataSource(dataSource);return transactionManager;}
}
- MybatisConfig配置类
public class MybatisConfig {@Bean //IoC容器中有SqlSessionFactory了public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) {//@Bean注入引用类型 特别方便 提供参数即可SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();//框架的好处,大部分东西都是默认的,不用设计,那些动态变化设置一下就ok了ssfb.setTypeAliasesPackage("cn.whu.domain"); //<typeAliases>ssfb.setDataSource(dataSource);//<dataSource>return ssfb;}//帮你造mapper 也就是Dao实现类的 (Service里的Autowired有东西自动注入了)@Bean //IoC容器中有 所有DaoImpl实现类了public MapperScannerConfigurer mapperScannerConfigurer() {MapperScannerConfigurer msc = new MapperScannerConfigurer();msc.setBasePackage("cn.whu.dao");//<mappers>return msc;}
}
- SpringConfig配置类
@Configuration
@ComponentScan({"cn.whu.service"})//有些该springMVC扫描 因此这里配置得细一点
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MyBatisConfig.class})
@EnableTransactionManagement //开启Spring事务管理
public class SpringConfig {}
1.2.3 Spring整合SpringMVC
- SpringMvcConfig配置类
@Configuration
@ComponentScan({"cn.whu.controller"})
@EnableWebMvc //很多辅助功能
public class SpringMvcConfig {}
- ServletContainersInitConfig 配置类,加载SpringMvcConfig和SpringConfig配置类
web容器刚刚启动时 就是加载这个配置类 也即是创建2个容器 A:SpringIOC容器和B:SpringMVCIOC容器
Spring和SpringMVC分别加载各自的Bean
B可以访问A但A不可以访问B
public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {// Spring核心配置文件protected Class<?>[] getRootConfigClasses() {return new Class[]{SpringConfig.class};}/*web容器启动时 加载这两个配置类 也即是创建2个容器 A:SpringIOC容器和B:SpringMVCIOC容器 Spring和SpringMVC分别加载各自的Bean B可以访问A但A不可以访问B*/// SpringMVC核心配置文件(SpringMVC的Controller就是我们的Servlet嘛)protected Class<?>[] getServletConfigClasses() {return new Class[]{SpringMvcConfig.class};}//配置SpringMVC拦截所有路径protected String[] getServletMappings() {return new String[]{"/"};}//Post请求乱码处理@Override //本质也是过滤器 (这里写新的方法,就相当于xml中写新的配置)protected Filter[] getServletFilters() {//可以自己写过滤器Filter,但是SpringMVC肯定有内置写好的CharacterEncodingFilter filter = new CharacterEncodingFilter();filter.setEncoding("utf-8");return new Filter[]{filter};}
}
整合配置完成了,启动服务器不报错,就OK啦。备份一下
2 功能模块开发
2.1 数据层开发(BookDao)
- Book实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Book {private Integer id;private String type;private String name;private String description;
}
- BookDao接口
接口方法 默认修饰符:public abstract
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();}
2.2 业务层开发(BookService/BookServiceImpl)
- BookService接口
JdbcConfig配置类中配置了PlatformTransactionManager
SpringConfig核心配置类中开启了注解式事务驱动 @EnableTransactionManagement
这里就可以直接一行注解开启事务了
@Transactional //表示所有方法进行事务管理
public interface BookService {//接口方法 默认修饰符:public abstract 所以不用写publicboolean save(Book book);boolean update(Book book);boolean delete(Integer id);Book getById(Integer id);List<Book> getAll();}
- BookServiceImpl实现类
@Service
public class BookServiceImpl implements BookService {@Autowiredprivate BookDao bookDao;public boolean save(Book book) {bookDao.save(book);return true;//抛出异常的时候 return false}public boolean update(Book book) {bookDao.update(book);return true;}public boolean delete(Integer id) {bookDao.delete(id);return true;}public Book getById(Integer id) {return bookDao.getById(id);}public List<Book> getAll() {return bookDao.getAll();}
}
2.3 表现层开发(BookController)
@RestController
@RequestMapping("/books")
public class BookController {@Autowiredprivate BookService service;//不返回String了 该返回啥就返回啥 异步交互的好处@PostMappingpublic boolean save(@RequestBody Book book){return service.save(book);}@PutMappingpublic boolean update(@RequestBody Book book) {return service.update(book);}@DeleteMapping("/{id}")public boolean delete(@PathVariable Integer id) {return service.delete(id);}@GetMapping("/{id}")public Book getById(@PathVariable Integer id) {return service.getById(id);}@GetMappingpublic List<Book> getAll() {return service.getAll();}}
3 接口测试
开发过程中有两个地方要停下来测试
1)Service开发完毕要停下来用Junit进行(Service)业务层接口测试
2)(Controller)表现层开发完毕要停下来用PostMan进行(Controller)表现层测试
3.1 Spring整合Junit测试业务层方法 (开发过程中Service开发完毕要停下来用Junit进行业务层接口测试)
@RunWith(SpringJUnit4ClassRunner.class)//Spring整合Junit专用的类加载器
@ContextConfiguration(classes = {SpringConfig.class})//加载spring核心配置类 把Spring用起来呀
public class BookServiceTest {@Autowiredprivate BookService service;@Testpublic void testSave() {Book book = new Book();book.setType("计算机");book.setName("springMVC入门");book.setDescription("小试牛刀");service.save(book);}@Testpublic void testGetById(){Book book = service.getById(1);System.out.println(book);}@Testpublic void testGetAll(){List<Book> books = service.getAll();System.out.println(books);}}
均能正常查询或者插入,后台没有问题啦~
3.2 postman测试表现层接口 (开发过程中表现层开发完毕要停下来用PostMan进行表现层测试)
先启动服务器呀
测试保存图书
测试修改图书
测试删除图书
测试根据Id查询图书
测试查询所有图书
二、表现层数据封装【重点】
问题导入
目前我们表现层响应给客户端的数据有哪几种?
1 表现层响应数据的问题
问题:我们表现层增删改方法返回true或者false表示是否成功,getById()方法返回一个json对象,getAll()方法返回一个json对象数组,这里就出现了三种格式的响应结果,极其不利于前端解析。
如果前后端都我们开发,这些格式其实很合理,但是现在前端不归我们写,那这些种类的返回格式得协商到什么时候啊,前后端肯定有一种统一的数据格式规范,不用怎么协商,直接就能看懂。
其实就是前后端人员通信的协议
eg:
解决:我们需要统一响应结果的格式
环境:将springmvc_08_ssm原封不动复制一份为springmvc_09_ssm_result即可
2 定义Result类封装响应结果
2.1 Result类封装响应结果
controller包下新建类Result.java
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Result {private Object data;private Integer code;private String msg;// 加一个双参public Result(Object data, Integer code) {this.data = data;this.code = code;}
}
注意事项:
Result类中的字段并不是固定的,可以根据需要自行增减
2.2 Code类封装响应码
controller包下新建类Code.java
//状态码
public class Code {// 1 结尾的表示成功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;// 0 结尾的表示失败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;
}
注意事项:
Code类的常量设计也不是固定的,可以根据需要自行增减,例如将查询再进行细分为GET_OK,GET_ALL_OK,GET_PAGE_OK
3 表现层数据封装返回Result对象
返回值类型全部改成刚刚设计的Result类型
@RestController
@RequestMapping("/books")
public class BookController {@Autowiredprivate BookService service;//全部统一返回Result,然后再把Result对象自动转成json@PostMappingpublic Result save(@RequestBody Book book) {boolean flag = service.save(book);return new Result(flag ? Code.SAVE_OK : Code.SAVE_ERR, flag);}@PutMappingpublic Result update(@RequestBody Book book) {boolean flag = service.update(book);return new Result(flag ? Code.UPDATE_OK : Code.UPDATE_ERR, flag);}@DeleteMapping("/{id}")public Result delete(@PathVariable Integer id) {boolean flag = service.delete(id);return new Result(flag ? Code.DELETE_OK : Code.DELETE_ERR, flag);}@GetMapping("/{id}")public Result getById(@PathVariable Integer id) {Book book = service.getById(id);Integer code = book != null ? Code.GET_OK : Code.GET_ERR;String msg = book != null ? "" : "数据查询失败,请重试!";return new Result(code, book, msg);}@GetMappingpublic Result getAll() {List<Book> bookList = service.getAll();//查询0条也可能成功,可能就是没有那种数据。 但是bookList==null 一定是抛异常失败了Integer code = bookList != null ? Code.GET_OK : Code.GET_ERR;String msg = bookList != null ? "" : "数据查询失败,请重试!";return new Result(code, bookList, msg);}}
三、异常处理器【理解】
问题导入
问题1:项目各个个层级均可能出现异常,异常处理代码书写在哪一层?
1 异常介绍
- 程序开发过程中不可避免的会遇到异常现象,我们不能让用户看到这样的页面数据
- 出现异常现象的常见位置与常见诱因如下:
- 框架内部抛出的异常:因使用不合规导致
- 数据层抛出的异常:因外部服务器故障导致(例如:服务器访问超时)
- 业务层抛出的异常:因业务逻辑书写错误导致(例如:遍历业务书写操作,导致索引异常等)
- 表现层抛出的异常:因数据收集、校验等规则导致(例如:不匹配的数据类型间导致异常)
- 工具类抛出的异常:因工具类书写不严谨不够健壮导致(例如:必要释放的连接长期未释放等)
思考1:各个层级均出现异常,异常处理代码书写在哪一层?
答1:所有得异常均抛出到表现层进行处理
思考2:异常种类千千万,若在表现层处理异常,每个方法中单独书写,代码书写量巨大且意义不强,如何解决?
答2:AOP思想呀
2 异常处理器
环境:springmvc_09_ssm_result原封不动复制一份为springmvc_09_ssm_result_exception即可
2.2.1 编写异常处理器
写在表现层Controller包下(因为是Controller要用啊,写在这里最合适了)
核心: 关键一个注解@RestControllerAdvice 容器里有他就能自动捕获Rest风格的Controller里的异常了
// springMVC扫描的就是"cn.whu.controller"包 所以此注解能被扫描到
@RestControllerAdvice //处理Rest风格开发的Controller
public class ProjectExceptionAdvice {@ExceptionHandler(Exception.class)//处理哪种类型的异常? Exception.class就统一处理所有种类的异常了public Result doException(Exception e) {System.out.println("异常哪里跑? " + e);return new Result(666, null, "异常哪里跑?");//返回值格式还得符合协议规范}
}
使用异常处理器之后的效果
也返回了统一格式的数据
2.2.2 @RestControllerAdvice注解介绍
名称:@RestControllerAdvice
类型:类注解
位置:Rest风格开发的控制器增强类定义上方
作用:为Rest风格开发的控制器类做增强(就是AOP)
说明:此注解自带@ResponseBody注解与@Component注解,具备对应的功能
2.2.3 @ExceptionHandler注解介绍
- 名称:@ExceptionHandler
- 类型:方法注解
- 位置:专用于异常处理的控制器方法上方
- 作用:设置指定异常的处理方案,功能等同于控制器方法,出现异常后终止原始控制器执行,并转入当前方法执行
- 说明:此类方法可以根据处理的异常不同,制作多个方法分别处理对应的异常
四、项目异常处理方案【理解】
问题导入
请说出项目当前异常的分类以及对应类型异常该如何处理?
1 项目异常分类
- 业务异常(BusinessException)
- 规范的用户行为产生的异常
- 不规范的用户行为操作产生的异常
- 系统异常(SystemException)
- 项目运行过程中可预计且无法避免的异常
- 其他异常(Exception)
- 编程人员未预期到的异常
2 项目异常处理方案
- 业务异常(BusinessException)
- 发送对应消息传递给用户,提醒规范操作
- 系统异常(SystemException)
- 发送固定消息传递给用户,安抚用户
- 发送特定消息给运维人员,提醒维护
- 记录日志
- 其他异常(Exception)
- 发送固定消息传递给用户,安抚用户
- 发送特定消息给编程人员,提醒维护(纳入预期范围内)
- 记录日志
3 项目异常处理代码实现
3.1 根据异常分类自定义异常类
新建一个包:exception,在里面写两个自定义异常
3.1.1 自定义项目系统级异常
//自定义异常处理器,用于封装异常信息,对异常进行分类
@Data
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;}}
3.1.2 自定义项目业务级异常
和上面一模一样 只是类名不同而已,继承的类都一样
//自定义异常处理器,用于封装异常信息,对异常进行分类
@Data
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;}}
3.2 自定义异常编码(持续补充)
controller.Code类里面加一些自定义编码
public class Code {//之前其他状态码省略没写,以下是新补充的状态码,可以根据需要自己补充public static final Integer SYSTEM_ERR = 50001;public static final Integer SYSTEM_TIMEOUT_ERR = 50002;public static final Integer SYSTEM_UNKNOWN_ERR = 59999;public static final Integer BUSINESS_ERR = 60002;}
3.3 触发自定义异常
实际开发肯定不这么干,而是用AOP干
@Service
public class BookServiceImpl implements BookService {@Autowiredprivate BookDao bookDao;//在getById演示触发异常,其他方法省略没有写进来// 下面代码只是为了模拟异常,所以看起来有点搞笑public Book getById(Integer id) {//比如所有异常都被我们分为2类,那么捕获到所有异常转成抛两类自定义异常//eg1: 模拟业务异常//将可能出现的异常进行包装,转换成自定义异常if(id < 0){throw new BusinessException(Code.BUSINESS_ERR,"请不要用你的技术挑战我的耐性!");}//eg2: 模拟系统异常//将可能出现的异常进行包装,转换成自定义异常try {int i = 1/0;}catch (Exception e){//怎么转换? 就是捕获到一切异常,这里都转换成throw new 自定义异常类(...)throw new SystemException(Code.SYSTEM_TIMEOUT_ERR,"服务器访问超时,请稍后再试试!",e);}return bookDao.getById(id);}
}
3.4 在异常通知类中拦截并处理异常
controller.ProjectExceptionAdvice
// springMVC扫描的就是"cn.whu.controller"包 所以此注解能被扫描到
@RestControllerAdvice //处理Rest风格开发的Controller
public class ProjectExceptionAdvice {// 这里统一拦截到系统异常,并做好处理@ExceptionHandler(SystemException.class)public Result doSystemException(SystemException e) {//记录日志//发送消息给运维//发送邮件给开发人员,ex对象发送给开发人员return new Result(e.getCode(),null,e.getMessage());//ex.getCode()是(ServiceImpl.getById)方法内throw new SystemException(code,"info",e); 时塞进来的code}// 这里统一拦截到业务异常,并做好处理@ExceptionHandler(BusinessException.class)public Result doBusinessException(BusinessException e) {return new Result(e.getCode(),null,e.getMessage());//ex.getCode()和e.getMessage()是(ServiceImpl.getById)方法内throw new SystemException(code,"info",e); 时塞进来的}@ExceptionHandler(Exception.class)//处理哪种类型的异常? Exception.class就统一处理所有种类的异常了public Result doException(Exception e) {//记录日志//发送消息给运维//发送邮件给开发人员,ex对象发送给开发人员return new Result(Code.SYSTEM_UNKNOWN_ERR,null,"系统繁忙,请稍后再试!");//ex.getCode()和e.getMessage()是(ServiceImpl.getById)方法内throw new SystemException(code,"info",e); 时塞进来的}}
测试:在postman中发送请求访问getById方法,传递参数-1,得到以下结果:
五、SSM整合页面开发【重点】
将springmvc_09_ssm_result_exception复制一份为springmvc_09_ssm_result_exception_page
这次打算加上页面
链接:https://pan.baidu.com/s/1opoo-lQ-DvqOFWN2mYsLSw
提取码:z0ae
复制好直接粘贴到webapp目录下
1 准备工作
为了确保静态资源能够被访问到,需要设置静态资源过滤
config包下新建文件SpringMvcSupport.java
@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {@Overrideprotected void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");registry.addResourceHandler("/css/**").addResourceLocations("/css/");registry.addResourceHandler("/js/**").addResourceLocations("/js/");registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");}
}
SpringMvcConfig的@ComponentScan要多扫描一个config包了 保证SpringMvcSupport 配置类能被扫描到
其实这两个配置类都可以直接创建成模板的
@Configuration
@ComponentScan({"cn.whu.controller","cn.whu.config"})//config是为了加载SpringMvcSupport类,以放行静态资源
@EnableWebMvc //很多辅助功能 eg: 自动类型转换 自动JSON格式转换
public class SpringMvcConfig {}
2 列表查询功能
- 前端代码
//列表
getAll() {//发送ajax请求axios.get("/books").then(resp => {//console.log(resp.data);this.dataList = data.data;})
},
就是这么简单
3 添加功能
- 前端代码
//弹出添加窗口
handleCreate() {this.dialogFormVisible = true;this.resetForm();//否则刚点击完并弹窗时表单会自动填充上次的数据
},//重置表单
resetForm() {this.formData = {}
},//添加
handleAdd() {axios.post("/books",this.formData).then(resp => {if(resp.data.code==20011){this.dialogFormVisible=false;this.$message.success("添加成功");}else if(resp.data.code==20010){this.$message.error("添加失败");}else {this.$message.error(resp.data.msg);}}).finally(()=>{//这里也可以写finallythis.getAll();})
},
- 后台代码改进
先把bookDao接口里的增删改方法返回值改成int
再让service按照影响行数返回true或者false
@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();}
}
- 测试: 将数据库表字段长度修改下限制
再添加type长度超过100的:
sql语句异常,直接走的SystemException(RuntimeException),被系统异常拦截了
4 修改功能
- 显示弹出框查询图书信息
//弹出编辑窗口
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);}});
}
我直接这么做的,其实也行,但是万一人家修改了前端页面,就可能出问题了,上面写法去查下数据库可以保证数据一定是数据库里原版的
//弹出编辑窗口
handleUpdate(row) {//直接就把行数据传送过来了 真好呀this.dialogFormVisible4Edit = true;this.formData = row;
},
- 保存修改后的图书信息
//编辑
handleEdit() {axios.put("/books", this.formData).then(res => {//如果操作成功,关闭弹层,显示数据if (res.data.code == 20031) {this.$message.success("修改成功!");this.dialogFormVisible4Edit = false;} else if (res.data.code == 20030) {this.$message.error("修改失败!");} else {this.$message.error(res.data.msg);}}).finally(() => {this.getAll();});
},
5 删除功能
// 删除
handleDelete(row) {//1. 弹出提示框this.$confirm('此操作将永久删除该行记录, 是否继续?', '提示', {type: 'warning'}).then(() => {//2. 做删除业务axios.delete("/books/"+row.id).then(res=>{if(res.data.code ==20021){this.$message.success("删除成功!");}else {this.$message.success("删除失败!");}}).finally(() => {this.getAll();});}).catch(() => {//3. 取消删除this.$message.info('已取消删除');});
},
六、拦截器【理解】
1 拦截器简介
问题导入
问题1:拦截器拦截的对象是谁?
问题2:拦截器和过滤器有什么区别?
1.1 拦截器概念和作用
- 拦截器(Interceptor)是一种动态拦截方法调用的机制,在SpringMVC中动态拦截控制器方法的执行
- 作用:
- 在指定的方法调用前后执行预先设定的代码
- 阻止原始方法的执行
- 总结:增强
- 核心原理:AOP思想
1.2 拦截器和过滤器的区别
- 归属不同:Filter属于Servlet技术,Interceptor属于SpringMVC技术
- 拦截内容不同:Filter对所有访问进行增强,Interceptor仅针对SpringMVC的访问进行增强
ServletContainersInitConfig里面配置了哪些请求需要走SpringMVC
假如配置成下面这样,那么只有/books/开头的请求走SpringMVC,Interceptor只能拦截这些请求,其他请求Interceptor管不到了 (Interceptor只能管SpringMVC的,管不了spring、mybatis以及其他一些框架的)
当然只是现在我们是直接这么配的,就很难看清二者区别了
protected String[] getServletMappings() { return new String[]{"/"}; }
2 入门案例
问题导入
定义拦截器需要实现什么接口?
2.0 环境准备
之前的springmvc_06_rest复制一份,改名为springmvc_12_interceptor,并删除不必要的文件
链接:https://pan.baidu.com/s/1tbXdjN_8PxzMPEwV01AdJA
提取码:roy6
2.1 拦截器代码实现
【第一步】定义拦截器
拦截怎么做?给个接口限制一下就OK啦,你就不会瞎写啦
做法:定义一个类,实现HandlerInterceptor接口即可
controller包下新建interceptor包,再在该包下新建ProjectInterceptor类,也就是
cn.whu.controller.interceptor.ProjectInterceptor
好处:@Component
注解就自动被扫描到了,因为也在Controller包下
@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;//return false;原始controller方法就不会再被访问执行了}@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...");}
}
【第二步】配置加载拦截器
config.SpringMvcSupport 之前的静态资源过滤放行(addResourceHandlers
)也写在这里
这么写 SpringMvcConfig 得多扫描一个包:
cn.whu.Controller
(下面写法2直接写在SpringMvcConfig中简单一点)
@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {//注入我们自己写的拦截器 [cn.whu.controller.interceptor.ProjectInterceptor]@Autowiredprivate ProjectInterceptor projectInterceptor;// 添加拦截器@Overrideprotected void addInterceptors(InterceptorRegistry registry) {// 请求/books (完全匹配) 时用这个拦截器拦截 【/boks/1 拦截不了哦】// 请求/books/* (eg:/books/1) 时也用这个拦截器拦截 【/books/s/1 拦截不到哦】// 请求/books/*/* (eg:/books/s/1) 时也用这个拦截器拦截registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*","/books/*/*");}
}
addPathPatterns是可变参数,随便写多少个
- 写法2: 使用标准接口WebMvcConfigurer简化开发(注意:侵入式较强 和spring强行绑定到一起了)
可以删除SpringMvcSupport配置类了,他的代码直接写在SpringMvcConfig里面,这样少了一个文件SpringMvcConfig 也可以少扫描一个"cn.whu.config"包
@Configuration
@ComponentScan({"cn.whu.controller"})
@EnableWebMvc
public class SpringMvcConfig implements WebMvcConfigurer {//SpringMvcSupport的内容直接写这里 上面不用再扫描 "cn.whu.config" 包了// 1、设置静态资源访问过滤,当前类需要设置为配置类,并被扫描加载public void addResourceHandlers(ResourceHandlerRegistry registry) {//当访问/pages/xxx的时候,走/pages目录下的内容registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");// 其他静态资源依次类推 逐一 放行registry.addResourceHandler("/css/**").addResourceLocations("/css/");registry.addResourceHandler("/js/**").addResourceLocations("/js/");registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");}//注入我们自己写的拦截器 [cn.whu.controller.interceptor.ProjectInterceptor]@Autowiredprivate ProjectInterceptor projectInterceptor;// 2、添加拦截器public void addInterceptors(InterceptorRegistry registry) {// 请求/books (完全匹配) 时用这个拦截器拦截 【/boks/1 拦截不了哦】// 请求/books/* (eg:/books/1) 时也用这个拦截器拦截 【/books/s/1 拦截不到哦】// 请求/books/*/* (eg:/books/s/1) 时也用这个拦截器拦截registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*","/books/*/*");}
}
2.2 拦截器流程分析
3 拦截器参数
问题导入
postHandle()和afterCompletion()方法都是处理器方法执行之后执行,有什么区别?
3.1 前置处理(▲ 实用性最强)
//原始方法调用前执行的内容
//返回值类型可以拦截控制的执行,true放行,false终止
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("preHandle..."+contentType);return true;
}
参数
- request:请求对象
- response:响应对象
- handler:被调用的处理器对象,本质上是一个方法对象,对反射技术中的Method对象进行了再包装
(简言之:handler是对原始执行方法的封装,有了它你就可以操作原始执行的方法)
返回值
返回值为false,被拦截的处理器将不执行。
//原始方法调用前执行的内容
//返回值类型可以拦截控制的执行,true放行,false终止
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//1、可以获取请求参数String contentType = request.getHeader("Content-Type");System.out.println(contentType);//2、特别好用的handlerSystem.out.println(handler);//cn.whu.controller.BookController#save(Book)System.out.println(handler.getClass());//class org.springframework.web.method.HandlerMethod//2.1 不妨强转试试HandlerMethod handlerMethod = (HandlerMethod) handler;// 哇:可以做反射里的一系列事情了System.out.println(handlerMethod.getBean());System.out.println(handlerMethod.getMethod());System.out.println("preHandle...");return true;
}
3.2 后置处理
//原始方法调用后执行的内容
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("postHandle...");
}
- 参数
modelAndView:如果处理器执行完成具有返回结果,可以读取到对应数据与页面信息,并进行跳转(了解)
注意:如果处理器方法出现异常了,该方法不会执行
3.3 完成后处理
//原始方法调用完成后执行的内容
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("afterCompletion...");
}
- 参数
ex:如果处理器执行过程中出现异常对象,可以针对异常情况进行单独处理
注意:无论处理器方法内部是否出现异常,该方法都会执行。
4 拦截器链配置
其实以后很少用多拦截器,一般一个拦截器就够了。
问题导入
什么是拦截器链?
4.1 多个拦截器配置
- 定义第二个拦截器
@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({"cn.whu.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/*");}
}
再访问任意路径,eg:http://localhost/books
4.2 多个连接器工作流程分析
- 当配置多个拦截器时,形成拦截器链
- 拦截器链的运行顺序参照拦截器添加顺序为准(SpringMvcConfig.addInterceptors方法里的添加顺序)
- 当拦截器中出现对原始处理器的拦截(return false),后面的(post)拦截器均终止运行
- 当拦截器运行中断,仅运行配置在前面的拦截器(他们return truel了)的afterCompletion操作
有一个return false了,所有的post都不会执行。
只要拦截器return true了,对应的after一定会执行 (return false的那个拦截器的after不会执行)
springMVC02-SSM整合(Result统一响应数据格式、异常页面修改、SSM整合vue-elementUI小案例、SpringMVC的拦截器Interceptor)相关推荐
- Day75.Ajax、拦截器Interceptor、异常映射、自动|手动类型转换、类型校验
目录 一.Ajax ★ 1. 基本类型参数传递 @ResponseBody响应体 2. Ajax传递实体类 3.Ajax传递实体类带级联属性 (非json 普通参数) @DateTimeFormat ...
- Result统一响应数据
目录 1. 响应数据的格式 1.1 状态码划分 1.3 响应数据的封装 2. 统一数据返回 1. 响应数据的格式 在分离的环境中,我们前后交互就显得尤为重要.前端按照接口文档中的URL地址和参数要求发 ...
- Spring Boot 统一功能处理(用户登录权限效验-拦截器、异常处理、数据格式返回)
文章目录 1. 统一用户登录权限效验 1.1 最初用户登录权限效验 1.2 Spring AOP 统一用户登录验证 1.3 Spring 拦截器 1.4 练习:登录拦截器 1.5 拦截器实现原理 1. ...
- spring Boot 2 基础篇 。内含 整合一个spring boot 的 小案例
目录 springBoot2基础篇 前言与开发环境 一.快速创建Boot项目 1.使用spring提供的快速构建 2.基于maven的手动构建 3.在Idea中隐藏指定文件/文件夹 二.SpringB ...
- SSM之SpringMVC 04 —— Ajax、拦截器、文件上传和下载
系列文章 SSM之SpringMVC 01 -- SpringMVC原理及概念.Hello SpringMVC 注解版和配置版 SSM之SpringMVC 02 -- Controller和RestF ...
- ssm项目jsp加载不出来图片_16. SSM 搭建
SSM:Spring.SpringMVC.Mybatis 项目完成图: 1.搭建 SpringMVC 1.1 导入spring和springMVC包 spring-webmvc servlet-api ...
- 【SSM框架系列】SpringMVC的文件上传、拦截器及异常处理
SpringMVC的文件上传 服务器端实现文件上传的技术有很多种,Servlet3.0,FileUtils,框架等等,都可以实现文件上传,不管使用哪一种上传技术,都必须满足三要素: 表单项type=& ...
- spring—拦截器和异常
SpringMVC的拦截器 SpringMVC拦截器-拦截器的作用 Spring MVC 的拦截器类似于 Servlet 开发中的过滤器 Filter,用于对处理器进行预处理和后处理. 将拦截器按一定 ...
- 玩转 SpringBoot 2 快速整合拦截器
概述 首先声明一下,这里所说的拦截器是 SpringMVC 的拦截器(HandlerInterceptor).使用SpringMVC 拦截器需要做如下操作: 创建拦截器类需要实现 HandlerInt ...
最新文章
- 不再写死,SpringBoot实现动态增删启停定时任务
- Scala:Enumeration
- Python多线程(1)——介绍
- 网转 mm IOS 报表
- 以太坊Bloom过滤器实现原理及应用场景分析
- 关于类微博的timeline的设计思考
- Sql Server对时间(月、周)的操作
- Julia:从交易流水动态计算可用资金
- lisp用entmake生产圆柱体_液态基酒生产
- 线性空间的向量组与数量矩阵的乘法
- openflow通信流程总结
- Tuxera NTFS如何解决硬盘无法写入文件教程分享
- 期货大佬给交易者的交易箴言,值得珍藏品读!
- 阿里云有奖调查结果公布,赠送10个阿里巴巴logo胸针...
- 个人开公司的流程及费用
- SAP PS常用事务代码T-CODE
- ICO和IPO的区别
- 真阳率(tp)、假阳率(np)
- qt使用assimp加载模型_有关Assimp与Qt3D
- workqueue --最清晰的讲解
热门文章
- 蔚来汽车交付量连续2个月下滑,理想汽车再登顶,小鹏汽车剑指9月
- prettier配置不生效问题
- 英格兰功勋老将或赛季后退役:身体已吃不消
- [golang gin框架] 29.Gin 商城项目-用户登录,注册操作
- 工业电子台账最简单的例子:设置模板后一键导入数据
- ubuntu16.04 ssh启用root连接
- Tensorflow 2.x源码详解之第三章:导数(梯度/GradientTape)
- [Overleaf] LaTeX中的斜体、加粗、下划线和简单指令
- jackson中@JsonProperty、@JsonIgnore等常用注解总结
- 华为鸿蒙系统的手机爆光图片,华为鸿蒙系统界面曝光,图标也实在太炫酷了,彻底摆脱安卓味道!...