当前位置:   article > 正文

SpringBoot统一功能处理

SpringBoot统一功能处理

拦截器

拦截器快速入门

什么是拦截器?

拦截器是Spring框架提供的核心功能之一, 主要用来拦截用户的请求, 在指定方法前后, 根据业务需要执行预先设定的代码.

也就是说, 允许开发人员提前定义一些逻辑, 在用户的请求响应前执行. 也可以在用户请求前阻止其执行. 就比如我们要通过一个url访问一个页面, 但是这个页面只有登录后才能访问, 这时就需要拦截, 比如你们可以试着登入这个url: CSDN.

然后就会被传送到这: 

在拦截器当中, 开发人员可以在应用程序中做一些通用性的操作, 比如通过拦截前端发来的请求, 判断Session中是否有登陆用户的信息. 如果有就可以放行, 如果没有就进行拦截. 

 比如抗日战争中, 想要在沦陷区进城, 就要向"太君"出示良民证(session),

出示了就可以放行, 放行之后还可能会有其它太君来对你搜身;   

没有出示"太君"就会大大咧咧地骂"八嘎!你滴, 滚远地干活".

 

 下面我们先来学习一下守城太君, 哎不对, 拦截器的基本使用.

拦截器的使用步骤分为两步:

1.定义拦截器(这个拦截器是用来干什么的, 比如守城太君不能让没带良民证的人进)

2.注册配置拦截器(拦截器要怎么做, 守城太君要查良民证)

自定义拦截器:实现HandlerInterceptor接口, 并重写其所有方法.

  1. @Slf4j
  2. @Component
  3. public class LoginInterceptorTest implements HandlerInterceptor {
  4. @Override
  5. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  6. log.info("LoginInterceptor 目标方法执行前执行..");
  7. return true;
  8. }
  9. @Override
  10. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
  11. log.info("LoginInterceptor 目标方法执行后执行..");
  12. }
  13. @Override
  14. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
  15. log.info("LoginInterceptor 视图渲染完毕后执行, 最后执行");
  16. }
  17. }

preHandle()方法: 目标方法执行前执行, 返回true: 继续执行后续操作; 返回false: 中断后续操作.

postHandle()方法: 目标方法执行后执行.

afterCompletion()方法: 视图渲染完毕后执行, 最后执行(后端开发现在几乎不涉及视图, 暂不了解).

注册配置拦截器: 实现WebMvcConfigurer接口, 并重写addInterceptors方法.

  1. @Configuration
  2. public class WebConfigTest implements WebMvcConfigurer {
  3. //自定义的拦截器对象
  4. @Autowired
  5. private LoginInterceptorTest loginInterceptorTest;
  6. @Override
  7. public void addInterceptors(InterceptorRegistry registry) {
  8. //注册自定义的拦截器对象
  9. registry.addInterceptor(loginInterceptorTest)
  10. .addPathPatterns("/**"); //设置拦截器拦截的请求路径(/**表示拦截所有路径)
  11. }
  12. }

 启动服务,  访问任意请求, 观察后端日志:

 可以看到preHandle方法执行之后就放行了, 开始执行目标方法, 目标方法执行完成之后执行postHandle和afterCompletion方法.

我们把拦截器中的preHandle方法的返回值改为false, 再观察运行结果:

可以看到, 拦截器拦截了请求, 没有进行响应.

拦截器详解

拦截器的入门程序完成之后, 接下来我们来介绍拦截器的使用细节. 拦截器的使用细节我们主要介绍两个部分:

1.拦截器的拦截路径配置
2.拦截器的实现原理

拦截路径

拦截路径是指我们定义的这个拦截器, 对哪些请求生效.

我们在注册配置拦截器的时候, 通过addPathPatterns()方法指定要拦截哪些请求. 也可以通过excludePathPatterns()指定不拦截哪些请求.

在拦截器中除了/**表示拦截所有资源之外, 还有一些常见的拦截路径设置:

拦截路径含义举例
/*一级路径能匹配/user, /book, /login, 不能匹配/user/login
/**任意级路径能匹配/user, /user/login, /user/reg
/book/*/book下的一级路径能匹配/book/addBook, 不能匹配/book/addBook/1, /book
/book/**/book下的任意级路径能匹配/book, /book/addBook, /book/addBook/2, 不能匹配/user/login

 以上拦截规则可以拦截此项目中使用的URL, 包括静态文件.

拦截器的执行流程

正常的调用顺序:

有了拦截器之后就是这样的流程:

 

1.添加拦截器后, 执行Controller方法之前, 请求会先被拦截器拦截住. 执行preHandle()方法, 这个方法需要返回一个布尔类型的值. 如果返回true, 就表示放行本次操作, 继续访问controller中的方法. 如果返回false, 就不会放行. (controller中的方法也不会执行).

2.controller当中的方法执行完毕之后, 再回来执行postHandle()这个方法以及afterCompletion()方法, 执行完毕后, 最终给浏览器响应数据. 

 适配器模式

适配器模式定义

适配器模式, 也叫包装器模式. 将一个类的接口, 转换成客户希望的另一个接口, 适配器让原本接口不兼容的类可以合作无间.

简单来说就是目标类不能直接使用, 通过一个新类包装一下, 适配调用方使用. 把两个不兼容的接口通过一定的方式使之兼容. 

比如下面两个接口, 本身是不兼容的(参数类型不一样, 参数个数不一样等等).

可以通过适配器, 使之兼容:
 

比如博主在大一时, 天气热了, 需要空调, 最后我们合资买了一个, 但是最后才知道学校规定, 必须要在指定平台租, 否则就不给办空调插口的电卡(非常sb的二求规定), 由于其它插口功率不高,不能直接给空调用, 我最后想了一个绝妙点子, 买了个功率转换头, 这里就可以通过其它非空调插口的插口直接使用. 这里这个功率转换头就是适配器.

适配器模式角色

Target: 目标接口(可以是抽象类或接口), 用户希望直接用的接口.(非空调插口的其它插口)

Adapee: 适配者, 但是与Target不兼容(空调)

Adapter: 适配器类, 通过继承或者引用适配者的对象, 把适配者转为目标接口.(功率转换头)

client: 需要使用适配器的对象.(劳累了一天非常热的灰灰)

适配器使用场景

一般来说, 适配器模式可以看作一种"补偿模式", 用于补救设计上的缺陷. 这种模式的应用算是"吾乃治具", 如果设计初期, 我们就能协调规避接口不兼容的问题, 就不需要使用适配器模式了.

所以适配器模式更多的应用场景主要是对正在运行的代码进行改造, 并且可以复用原有代码实现新功能.

统一数据返回格式

统一数据返回格式是指在软件开发中约定一种统一的数据结构格式, 用于向客户端或者调用方返回数据. 这种格式通常包含固定的字段, 以便客户端能够统一地解析和处理返回的数据, 从而提高开发效率和降低沟通成本. 

快速入门

统一的数据返回格式使用@ControllerAdvice和ResponseBodyAdvice的方式实现@ControllerAdvice表示控制器通知类.

添加类ResponseAdvice, 实现ResponseBodyAdvice接口, 并在类上添加@ControllerAdvice注解.

  1. @ControllerAdvice
  2. public class ResponseAdviceTest implements ResponseBodyAdvice {
  3. @Override
  4. public boolean supports(MethodParameter returnType, Class converterType) {
  5. return true;
  6. }
  7. @Override
  8. public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectContentType,
  9. Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
  10. return Result.success(body);
  11. }
  12. }

supports方法: 判断是否要执行beforeBodyWrite方法. true为执行, false不执行. 通过该方法可以选择哪些类或哪些方法的response要进行处理, 其它的不进行处理. 

beforeBodyWrite方法: 对response方法进行具体操作处理.

 存在问题

这里就直接开门见山: 首先提供以下代码:

  1. @RequestMapping("/test")
  2. @RestController
  3. public class TestController {
  4. @RequestMapping("/t1")
  5. public String t1() {
  6. return "t1";
  7. }
  8. @RequestMapping("/t2")
  9. public boolean t2() {
  10. return true;
  11. }
  12. @RequestMapping("/t3")
  13. public Integer t3() {
  14. return 200;
  15. }
  16. }

将以上代码的每一个入口都走一遍(先走t2t3,最后t1), 我们发现:

当返回类型为String时, 这里报错了. 

结论: 当返回类型为String时, 就会发生错误.

 解决方案:

  1. @ControllerAdvice //这时一个用于定义全局控制器通知的注解
  2. public class ResponseAdvice implements ResponseBodyAdvice {
  3. @Autowired
  4. //用于java对象和json格式之间的转换
  5. private ObjectMapper objectMapper;
  6. @Override
  7. //supports方法: 判断是否要执行beforeBodyWrite方法, true为执行, false为不执行.
  8. //通过该方法可以选择哪些类或哪些方法的response要进行处理, 其它的不处理
  9. public boolean supports(MethodParameter returnType, Class converterType) {
  10. return true;
  11. }
  12. @SneakyThrows
  13. @Override
  14. //该方法用于实际处理响应体
  15. public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType,
  16. ServerHttpRequest request, ServerHttpResponse response) {
  17. //检查是否是Result类型, 如果是的话,则直接返回, 因为已经是统一的格式了.
  18. if(body instanceof Result) {
  19. return body;
  20. }
  21. //如果返回结果为String类型, 使用内部的SpringBoot内置提供的Jackson来实现信息的序列化
  22. if(body instanceof String) {
  23. return objectMapper.writeValueAsString(Result.success(body));
  24. }
  25. //如果既不是Result类型又不是String类型, 就将其包装成Result.success(body)的形式返回
  26. return Result.success(body);
  27. }
  28. }

再次运行返回为String的接口, 正常. 

优点

1.方便前端程序员更好地接受和解析后端数据接口返回的数据

2.降低前端程序员和后端程序员沟通成本, 按照某个格式实现即可, 因为所有的接口都是这样返回的

3.有利于项目统一数据的维护和修改.

4.有利于后端技术部门的统一规范的标准制定, 不会出现稀奇古怪的返回内容.

统一异常处理

统一异常处理使用的是@ControllerAdvice + @ExceptionHandler来实现的, @ControllerAdvice表示控制器通知类, @ExeptionHandler是异常处理器, 两个结合表示当出现异常时执行某个通知, 也就是执行某个方法事件.

具体执行如下:

  1. @ControllerAdvice
  2. @ResponseBody
  3. public class ErrorAdvice {
  4. @ExceptionHandler
  5. public Object handler(Exception e) {
  6. return Result.fail(e.getMessage());
  7. }
  8. }

类名, 方法名和返回值都可以自定义, 重要的是注解.

接口返回为数据时, 需要加@ResponseBody注解.

以上代码表示, 如果代码出现Exception异常(包括Exception的子类), 就返回一个Result对象, Result对象的设置参考Result.fail(e.getMessage()).

  1. public static Result fail(String msg) {
  2. Result result = new Result<>();
  3. result.setErrMsg(msg);
  4. result.setCode(ResultStatus.FAIL);
  5. return result;
  6. }

 我们可以针对不同的异常, 返回不同的结果:

  1. @Slf4j
  2. @ResponseBody
  3. //控制器通知类
  4. @ControllerAdvice
  5. //统一处理异常
  6. public class ExceptionAdvice {
  7. @ExceptionHandler
  8. public Result handerException(Exception e) {
  9. log.error("发生异常, e:()", e);
  10. return Result.fail("内部错误");
  11. }
  12. @ExceptionHandler
  13. public Result handlerException(NullPointerException e) {
  14. log.error("发生异常: e", e);
  15. return Result.fail("发生空指针异常");
  16. }
  17. @ExceptionHandler
  18. public Result handlerException(ArithmeticException e) {
  19. log.error("发生异常, e:", e);
  20. return Result.fail("发生算术异常");
  21. }
  22. }

 模拟制造异常:

  1. @RequestMapping("/test")
  2. @RestController
  3. public class TestController {
  4. @RequestMapping("/t1")
  5. public String t1() {
  6. return "t1";
  7. }
  8. @RequestMapping("/t2")
  9. public boolean t2() {
  10. int a = 10 / 0; // 抛出ArithmeticException
  11. return true;
  12. }
  13. @RequestMapping("/t3")
  14. public Integer t3() {
  15. String a = null;
  16. System.out.println(a.length()); // 抛出NullPointerException
  17. return 200;
  18. }
  19. }

 运行t2:

 运行t3:

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/516786
推荐阅读
相关标签
  

闽ICP备14008679号