• 拦截器以及统一功能的实现


    目录

    引言 

    实现一个简单的拦截器

    拦截器小结

    统一访问前缀

     统一异常处理

    统一返回参数

    @ControllerAdvice


    引言 

     HandlerInterceptor是Spring MVC框架提供的一个拦截器接口,它用于对请求进行拦截和处理。在Spring MVC中,拦截器可以用于实现一些通用的功能,比如日志记录、权限验证、请求参数预处理等。

    HandlerInterceptor接口定义了三个方法:

    1. preHandle:在进入Controller之前执行,返回值表示是否继续执行后续的拦截器和Controller。可以通过该方法进行一些前置处理,比如登录验证、权限检查等。

    2. postHandle:在调用Controller之后,渲染视图之前执行。可以通过该方法对ModelAndView进行修改,或者将公共的模型数据添加到视图中。

    3. afterCompletion:在整个请求完成后执行,包括视图渲染完成和响应已经返回。可以通过该方法进行一些资源清理操作,比如释放数据库连接、删除临时文件等。

    需要注意的是,拦截器可以配置多个,按照配置的顺序依次执行。当某个拦截器的preHandle方法返回false时,后续的拦截器和控制器将不再执行,请求将直接返回。

    在使用HandlerInterceptor时,需要配置拦截器并将其注册到Spring MVC的配置中。可以通过实现HandlerInterceptor接口来自定义拦截器,并在配置中进行注册。

    拦截器的使用可以有效地实现横切关注点,比如登录验证、日志记录等,与AOP相比,拦截器更加适用于一些与请求相关的处理,而AOP更适合于业务逻辑解耦。

    但是,在实际开发中,我们通常需要在多个应用程序中使用相同的功能,例如日志记录、异常处理、事务控制等。如果每个应用程序都编写自己的实现,将会导致代码重复、维护困难等问题。因此,Spring Boot 提供了一系列机制来实现统一功能,以便在不同的应用程序中共享代码和功能。

    具体来说,Spring Boot 支持以下几种方式来实现统一功能

    1. 1 AOP

    Spring Boot 中使用 AOP 实现统一功能的方式与普通 Spring 应用程序相同,可以通过配置切面类来实现日志记录、事务控制等功能。这篇文章中讲述的拦截器就可以实现切面编程

    1. 2. 过滤器

    Spring Boot 支持使用过滤器来实现切面编程。我们可以使用 javax.servlet.Filter 接口定义一个过滤器,并在应用程序启动时自动注册该过滤器。

    1. 3. 拦截器

    Spring Boot 还支持使用拦截器来实现切面编程。我们可以使用 org.springframework.web.servlet.HandlerInterceptor 接口定义一个拦截器,并在应用程序启动时自动注册该拦截器。

    1. 4. 控制器建议

    控制器建议是 Spring Boot 中另一个实现统一功能的方式。我们可以使用 org.springframework.web.bind.annotation.ControllerAdvice 注解快速定义一个控制器建议类,并在其中实现统一处理请求异常、返回值等功能。

    综上所述,Spring Boot 提供了多种方式来实现统一功能,这些机制使得代码重用和维护变得更加容易和高效。

    拦截器在请求进入控制器之前或之后对请求进行预处理或后处理

    实现一个简单的拦截器

    1. 写一个拦截器类继承HandlerInterceptor,并且重写preHandle方法

    1. package com.example.demo.config;
    2. import org.springframework.context.annotation.Configuration;
    3. import org.springframework.web.servlet.HandlerInterceptor;
    4. import javax.servlet.http.HttpServletRequest;
    5. import javax.servlet.http.HttpServletResponse;
    6. import javax.servlet.http.HttpSession;
    7. /**1.这是一个普通的拦截器
    8. *
    9. * 判断用户登录;
    10. * 1。1继承HandleInterceptor;
    11. * 1.2.重写preHeadler
    12. * */
    13. /**2.将拦截器添加到配置文件中,并且设置拦截的规则
    14. *
    15. * */
    16. @Configuration
    17. //拦截器
    18. public class LoginInterceptor implements HandlerInterceptor {
    19. @Override
    20. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    21. //用户登录业务判断
    22. HttpSession session=request.getSession(false);
    23. if(session!=null&&session.getAttribute("userinfo")!=null){//才开始我写了||怪不得会空指针异常,
    24. System.out.println("登陆成功");
    25. return true;
    26. }
    27. response.sendRedirect("/login.html");
    28. //response.setStatus(403);
    29. return false;
    30. }
    31. }

    这里如果失败 重定向页面到login.html,

    1. "en">
    2. "UTF-8">
    3. "viewport"
    4. content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    5. "X-UA-Compatible" content="ie=edge">
    6. 登陆页面
    7. 登陆页面

    2. 将上述拦截器添加到系统配置文件中,并且设置拦截的规则

    1. package com.example.demo.config;
    2. import org.springframework.beans.factory.annotation.Autowired;
    3. import org.springframework.context.annotation.Configuration;
    4. import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    5. import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    6. /**将拦截器添加到配置文件并且设置拦截的规则
    7. * */
    8. @Configuration
    9. public class AppConfig implements WebMvcConfigurer {
    10. @Autowired
    11. LoginInterceptor loginInterceptor;
    12. @Override
    13. public void addInterceptors(InterceptorRegistry registry) {
    14. registry.addInterceptor(loginInterceptor)//这里也可以只写一个new LoginIntercepter;
    15. .addPathPatterns("/**")//拦截所有请求
    16. .excludePathPatterns("/user/login")//登陆不用拦
    17. .excludePathPatterns("/user/req")
    18. .excludePathPatterns("/**/*.html")//后缀是html
    19. .excludePathPatterns("login.html");
    20. }
    21. }

    3.这里写入登录注册类无需被拦截,写入另一个会被拦截的类来对比

    1. package com.example.demo.controller;
    2. import org.springframework.web.bind.annotation.RequestMapping;
    3. import org.springframework.web.bind.annotation.RestController;
    4. @RestController
    5. @RequestMapping("/user")
    6. public class UserController {
    7. @RequestMapping("/getuser")
    8. public String getuser(){
    9. System.out.println("执行了getuser");
    10. return "getuser";
    11. }
    12. @RequestMapping("/login")//登录和注册不用被拦截
    13. public String login(){
    14. System.out.println("执行 login方法");
    15. return " login";
    16. }
    17. @RequestMapping("/reg")
    18. public String reg(){
    19. System.out.println("执行 reg");
    20. return "reg___";
    21. }
    22. }

     此时当我们访问127.0.0.1:8080/user/getuser就会由于被拦截而跳转到login.html

    拦截器小结

    所有的controller请求都都会通过DispatcherServlet(调度器)拦截器。

    通过调度器源码,我们发现调度器会先进行(拦截器)预处理,如果不满足就不要往下执行。然后执行controller中的业务。拦截器也是通过动态代理和环绕通知实现的

    *调度器:当客户端发送请求时,请求首先到达调度器(DispatcherServlet)。调度器会根据配置的请求映射规则,将请求转发给对应的控制器。控制器接收请求后,根据具体的业务逻辑进行处理,并生成相应的响应结果。起到了请求分发和控制器调度的作用,使得请求能够被正确地路由到相应的控制器进行处理。就是起到了请求分发和控制器调度的作用,使请求能够正确路由到相应控制器

    统一访问前缀

    就在程序中输入 confiure它会自动提示你的,我们只需要重写他的方法,有时候根据路由访问不到,可能就是因为这个

    1. @Override
    2. public void configurePathMatch(PathMatchConfigurer configurer) {
    3. configurer.addPathPrefix("/zhangsan",c -> true);
    4. /**我们把前面拦截器中的代码去掉,就可以根据/zhangsan/user/getuser去访问,
    5. *但是user/getuser是不可以的,有点疑问
    6. * */
    7. /**
    8. * 还可以在properties文件中server.servlet.context-path=/lisi
    9. * */
    10. }

     统一异常处理

    在Spring Boot中,可以通过定义@ControllerAdvice注解的类来实现全局的异常处理。 @ControllerAdvice是一个增强的Controller,使用它可以实现三个方面的功能:

    1. 全局异常处理
    2. 全局数据绑定
    3. 全局数据预处理


    @ExceptionHandler是Spring框架提供的一种异常处理机制,用于捕获Controller中抛出的异常,并处理这些异常

    通常,当Controller中的方法抛出异常时,Spring框架会将异常转换为HTTP响应中的错误码和错误消息(例如404 Not Found、500 Internal Server Error等)。使用@ExceptionHandler可以覆盖默认的异常处理逻辑,自定义如何处理异常,并返回更有意义的错误信息给客户端  


    我们重点关注第一个方面,也就是全局异常处理。下面是实现全局异常处理的步骤:

    1. 创建一个标注了@ControllerAdvice注解的类,该类需要使用@ExceptionHandler注解来处理异常。

    2. 在@ExceptionHandler注解中指定要处理的异常类型。

    3. 在@ExceptionHandler注解中编写具体的异常处理代码,比如返回错误信息或者跳转到错误页面等。


    具体实现方法可以参考以下代码:

    1. package com.example.demo.config;
    2. //自定义异常类
    3. import org.springframework.web.bind.annotation.ControllerAdvice;
    4. import org.springframework.web.bind.annotation.ExceptionHandler;
    5. import org.springframework.web.bind.annotation.ResponseBody;
    6. import java.util.HashMap;
    7. @ControllerAdvice// 1.用于定义全局的异常处理器和全局数据绑定规则。@ControllerAdvice只能处理当前应用上下文范围内的Controller中的异常。如果要跨应用程序上下文使用,则应考虑使用@RestControllerAdvice。
    8. @ResponseBody
    9. public class MyExHandle {
    10. @ExceptionHandler(Exception.class)//2.一种异常处理机制,用于捕获Controller中抛出的异常,并处理这些异常
    11. public HashMap nullException(NullPointerException e){
    12. HashMap result=new HashMap<>();
    13. result.put("code","-1");
    14. result.put("msg"," 空指针异常 "+e.getMessage());
    15. result.put("data",null);
    16. return result;
    17. }
    18. }
    1. @RequestMapping("/login")//登录和注册不用被拦截
    2. public String login(){
    3. Object o=null;
    4. o.hashCode();//在这里我们想要有空指针异常的时候返回给前端一个值,所以定义了一个异常
    5. /**
    6. * {
    7. * "msg": " 空指针异常 null",
    8. * "code": "-1",
    9. * "data": null
    10. * }*/
    11. System.out.println("执行 login方法");
    12. return " login";
    13. }

    统一返回参数

    /**统一数据格式的返回
     * 1.@ControllerAdvice
     * 2.实现ResponseBody 接口
     * 3.重写接口中的方法 supports和beforeBodyWrite
     * */ 

    这里是初版和改进版但并不是最终版

    1. package com.example.demo.config;
    2. import org.springframework.core.MethodParameter;
    3. import org.springframework.http.MediaType;
    4. import org.springframework.http.server.ServerHttpRequest;
    5. import org.springframework.http.server.ServerHttpResponse;
    6. import org.springframework.web.bind.annotation.ControllerAdvice;
    7. import org.springframework.web.bind.annotation.ResponseBody;
    8. import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
    9. import java.util.HashMap;
    10. @ControllerAdvice
    11. public class ResponseAdvice implements ResponseBodyAdvice {
    12. /**此方法返回true,则执行下面的beforeBodyWrite方法;
    13. *false就不执行喽
    14. * */
    15. @Override
    16. public boolean supports(MethodParameter returnType, Class converterType) {
    17. return true;//
    18. }
    19. @Override
    20. public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
    21. HashMap result=new HashMap<>();
    22. result.put("code",200);
    23. result.put("msg","");
    24. result.put("data",body);
    25. return result;
    26. }
    27. }
    1. @RequestMapping("/getnum")
    2. public Integer getNumber(){
    3. System.out.println("哈哈哈哈");
    4. return new Random().nextInt(10);
    5. /**{"msg":"","code":200,"data":7}
    6. * */
    7. }

     java.lang.ClassCastException: java.util.HashMap cannot be cast to java.lang.String],

    因为HashMapString是不兼容的类型所以会报错。如果需要将HashMap转换为字符串表示,仍然需要编写逻辑来进行相应的转换操作,例如使用StringBuilder工具类来构建字符串表示。


    使用jackson

    Jackson是一个流行的Java库,用于处理JSON格式的数据。它提供了一组功能强大的API,可以实现JSON和Java对象之间的转换。

    在您的代码示例中,通过@Autowired注解将Jackson的ObjectMapper类注入到ResponseAdvice类中。这样可以在beforeBodyWrite方法中使用ObjectMapper对象将返回的响应数据转换为JSON格式。

    在beforeBodyWrite方法中,首先创建一个HashMap对象result,并将code、msg和data放入其中。然后,根据body的类型进行判断:

    • 如果body是String类型,将result对象转换为JSON字符串,并返回;
    • 否则,直接返回result对象。

    这样,在控制器方法返回的数据在经过ResponseAdvice处理后,都会按照统一的数据格式进行返回。

     
    1. package com.example.demo.config;
    2. import com.fasterxml.jackson.core.JsonProcessingException;
    3. import com.fasterxml.jackson.databind.ObjectMapper;
    4. import org.springframework.beans.factory.annotation.Autowired;
    5. import org.springframework.core.MethodParameter;
    6. import org.springframework.http.MediaType;
    7. import org.springframework.http.server.ServerHttpRequest;
    8. import org.springframework.http.server.ServerHttpResponse;
    9. import org.springframework.web.bind.annotation.ControllerAdvice;
    10. import org.springframework.web.bind.annotation.ResponseBody;
    11. import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
    12. import java.util.HashMap;
    13. /**统一数据格式的返回
    14. * 1.@ControllerAdvice
    15. * 2.实现ResponseBody 接口
    16. * 3.重写接口中的方法 supports和beforeBodyWrite
    17. *
    18. *
    19. * */
    20. @ControllerAdvice
    21. public class ResponseAdvice implements ResponseBodyAdvice {
    22. /**
    23. * Jackson是一个流行的Java库,用于处理JSON格式的数据。它提供了一组功能强大的API,可以实现JSON和Java对象之间的转换。
    24. * */
    25. @Autowired
    26. private ObjectMapper objectMapper;
    27. /**此方法返回true,则执行下面的beforeBodyWrite方法;
    28. *false就不执行喽
    29. * */
    30. @Override
    31. public boolean supports(MethodParameter returnType, Class converterType) {
    32. return true;//
    33. }
    34. @Override
    35. public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
    36. HashMap result=new HashMap<>();
    37. result.put("code",200);
    38. result.put( "msg","");
    39. result.put("data",body);
    40. if(body instanceof String){
    41. //String转换的时候会报错
    42. try {
    43. return objectMapper.writeValueAsString(result);
    44. } catch (JsonProcessingException e) {
    45. e.printStackTrace();
    46. }
    47. }
    48. return result;
    49. }
    50. }

    @ControllerAdvice

    派生于@Component,用于定义全局的异常处理、统一数据处理等 。它的具体实现是通过组合多个注解和解析类来实现不同的功能。

    1. @Controller注解通常用于标记处理请求的控制器类,处理业务逻辑并返回响应数据。例如,处理用户登录、查询数据等请求。
    2. @ControllerAdvice注解通常用于对全局进行增强处理。例如,统一处理异常情况、预处理请求数据,统一数据格式等。

    通过源码查看,这个方法在执行会查找所有的@ControllerAdvice类,这些类会被容器中,当发生某个事件时,调用相应的Advice方法,如:发生异常时调用异常地Advice方法实现;

    序列化和反序列化

    序列化和反序列化是两个与数据存储和传输相关的概念,分别表示将数据从一种格式转换为另一种格式的过程。

    **序列化**

    序列化是指将一个对象转换为字节序列的过程,使得它可以在网络上传输或者存储到本地磁盘中。序列化后的数据可以被保存下来,以便在需要时再进行反序列化恢复成原来的对象。在 Java 中,序列化通常使用 `ObjectOutputStream` 类实现,可以将一个对象序列化为二进制数据流或者文本形式的字符串,例如 JSON 或 XML 格式。

    **反序列化**

    反序列化是指将一个序列化的对象恢复成原来的对象的过程。在 Java 中,反序列化通常使用 `ObjectInputStream` 类实现,可以将一个二进制数据流或者文本形式的字符串反序列化为一个 Java 对象。在反序列化过程中,Java 虚拟机会根据序列化时的规则和信息,重新构造出原来的对象,并赋上相应的属性值。

    序列化是将对象转变为字节流,通常适用于网络传输和存储

    本文介绍了统一用户登录权限的效验使用 WebMvcConfigurer + Handlerlnterceptor 来实现,统一异常处理使用@ ControllerAdvice +@ ExceptionHandler 来实现,统一返回值处理使用@ ControllerAdvice +实现ResponseBodyAdvice接口并且重写两个方法 来处理。 

  • 相关阅读:
    Day04NTFS安全权限、文件共享服务器、破解winxp系统密码
    为什么不应该在 Flutter 中使用全局变量
    数码管时钟--LABVIEW编程
    腾讯云4核8G12M轻量服务器优惠价格446元一年,646元15个月
    Angular tsconfig.json 文件里的 paths 用途
    里程碑!用自己的编程语言实现了一个网站
    Cookie/Session
    情人节程序员用HTML网页表白【粒子告白】 HTML5七夕情人节表白网页源码 HTML+CSS+JavaScript
    PCB设计时如何选择合适的叠层方案
    解决QT编译Android程序不支持openssl问题
  • 原文地址:https://blog.csdn.net/m0_74106420/article/details/133933374