文章目录
- 一、什么是统一功能
- 二、拦截器
- 2.1 什么是拦截器
- 2.2 拦截器的使用
- 2.3 案例:不拦截前端的请求
- 2.4 拦截器是如何实现的 ---- >分析DispatcherServlet源码分析
- 三、适配器模式
- 四、统一数据返回格式
- 五、统一异常
- 六、案例:在图书管理系统使用统一功能
一、什么是统一功能
- 什么是统一功能:大家都要做的事情,每一个接口都要执行这个操作
- 统一功能包含两个部分:
- (1)Spring本身提供的统一功能
- (2)Spring本身并没有提供某个统一功能,但我们又需要,如何去自定义
二、拦截器
2.1 什么是拦截器
- 什么是拦截器:主要用来拦截用户的请求,在指定方法前后,根据业务需要执行预先设定的代码。
- 统一功能之一:是Spring提供的统一功能的其中之一,可以公共地对一个信息进行处理
- 作用维度:拦截器的作用维度是URL,像Service代码没有配置我们是拦截不了的
- 使用拦截器后的执行流程
- 请求:之前请求过来,先进入Controller,如果有拦截器,是先进入拦截器看有没有需要预先执行的方法,如果有且拦截器放行了,才会去执行Controller里的代码
- 响应:返回时,当Controller里的方法全部执行完,会被拦截器拦截,执行拦截器中设置的【响应后的代码】
2.2 拦截器的使用
@Configuration
public class webConfig implements WebMvcConfigurer { //WebMvcConfigurer是一个Mvc的配置器
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor) //添加拦截器
.addPathPatterns("/**") //要拦截哪些方法
.excludePathPatterns(""); //哪些不进行拦截
}
}
@Component
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("目标方法执行前");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("目标方法执行后");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
- 关于路径的定义:addPathPatterns 和 excludePathPatterns里放的是网络路径,而非文件路径
- /*:一级路径
- 能匹配/user、/book
- 不能匹配/user/login
- /**:任意级路径
- 能匹配/user、/user/login、/book/addBook,所有路径都能匹配
- /book/*:/book下的一级路径
- 能匹配/book/addBook
- 不能匹配/book/addBook/1,/book
- /book/**:/book下的任意级路径
- 能匹配/book、/book/addBook
- 不能匹配/user/login
- /*:一级路径
2.3 案例:不拦截前端的请求
- 问题描述:
- 因为此时除了【/user/login】这个后端请求,所有的请求都被拦截了,这包括了前端的所有请求(甚至图片的访问都被拦截了)
- 比如我们无法通过在浏览器输入【http://127.0.0.1:8080/login.html】来访问这个网页并进行后续的登录操作
@Configuration
public class webConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/user/login");
}
}
- 解决方法:把前端的访问路径通过【excludePathPatterns()】排除掉
- 关于excludePathPatterns()的其他写法:
2.4 拦截器是如何实现的 ---- >分析DispatcherServlet源码分析
- 源码如何阅读:找核心代码,不要钻牛角尖。一行一行地细细研究,很快就会忘记当前在哪了。
- 阅读方法:切忌从头到尾一行一行地去读代码,一点一点去研究,一定要找到关键代码后,在宏观上对整个流程或整个原理有一个认识后,有时间再去看里面的细节
- 快捷键:如果我们看调用代码时,忘记了前后调用的顺序,可以通过下面两个快捷键来操作
- ctrl + [:回到上一步操作
- ctrl + ]:来到下一步操作
- 步骤:
-
程序启动后,拦截器会初始化DispatcherServlet
-
DispatcherServlet的 init 阶段:
-
DispatcherServlet的 service 阶段:
-
DispatcherServlet的 destory 阶段:
-
三、适配器模式
-
什么是适配器模式:
-
适配器模式的实现:
- 此处我们模拟Slf4j:@Slif4j既是一个典型的模型模式,也是一个适配器模式
- 一个框架用到的模式是非常多的,可能源码的短短几行代码就能体现出2,3种设计模式
- 代码:
- 为什么用户不能直接调log4j和slf4j:不能保证所有的人都用所有的日志框架,项目之间可能会有冲突,需要有一个门面
- 为什么用户不能直接调log4j和slf4j:不能保证所有的人都用所有的日志框架,项目之间可能会有冲突,需要有一个门面
- 此处我们模拟Slf4j:@Slif4j既是一个典型的模型模式,也是一个适配器模式
-
应用场景:版本升级
- 适配器模式是一种补偿模式,不是一开始就有的,是后期设计上的缺陷没办法修改了的打补丁式的无奈之举
- 所以适配器的应用场景主要是对正在运行的代码进行改造,并且希望复用原有代码实现新的功能,比如版本升级等
四、统一数据返回格式
-
效果:使每一个接口返回的数据都是同种格式的
-
实现方法:使用@ControllerAdvice注解 + ResponseBodyAdvice接口 + 重写两个方法
-
当前代码存在的问题:
-
为什么返回结果为String时,后端无法正确执行:
-
上述问题的解决方法:
-
为什么用了ObjectMapper后,可以解决String的问题:
-
统一数据返回格式的优点:
- 由于返回的数据都变成了Result,方便前后端沟通,前端人员知道返回的数据是什么样的
- 后端代码不需要太多的改动,就可以统一返回格式
五、统一异常
-
为什么要有统一异常:自己的错误自己知道即可,不给外界知道
- 对于后端而言:我们不希望把一些诸如空指针的错误放给前端看
- 对于前端而言:也希望不给用户看太细致的错误,而是统一跳转到一个页面,比如内部出错了,找不到页面之类的
-
什么时候统一异常不会生效:如果代码已经手动用try-catch捕获了,就不会被我们写的【统一异常处理】捕获到了,如果没有手动捕获,才会被统一处理
-
针对的对象:主要针对的是运行时异常
-
代码:
- 固定的三个注解:@ResponseBody、@ControllerAdvice、@ExceptionHandler
- @Slf4j:建议加上日志,方便后续找错
-
@ControllerAdvice注解分析
-
为什么统一异常代码能生效:
-
如何阅读源码:注意源码的版本匹配
- 方式一:IDEA上点击Download Source
- 方式二: 去Spring的github上查看
六、案例:在图书管理系统使用统一功能
- 登录:登录没有进行拦截,所以没有连接失败的情况(不需要写error),只需要更改前端代码即可
- 返回图书列表:
- 添加图书: