• 【Spring AOP】统一处理过程 代码实现


    上一节我们讲的是Spring AOP的原理部分,介绍了切面,切点,连接点,通知,织入,以及简单的代码实现.

    我们上一节是使用@Aspect来进行切点函数的范围和通知方法的设定的.

    但是使用上面的方法有两个问题是无法解决的;

    1. 无法获取HttpSession
    2. 切点的execution函数的拦截语法还是有限的,不可以清楚的定位

    所以,我们就需要使用新的方法.

    SpringAop给我们提供了新的框架------Spring 拦截器

    Spring拦截器

    Spring 中提供了具体的实现拦截器:HandlerInterceptor,
    拦截器的实现分为以下两个步 骤:

    1. 创建⾃定义拦截器,实现 HandlerInterceptor 接⼝的 preHandle(执⾏具体⽅法之前的预处理)⽅ 法。
    2. 将⾃定义拦截器加⼊ WebMvcConfigurer 的 addInterceptors ⽅法

    1. 自定义拦截器

    1. 创建一个拦截器类
    2. 实现HandlerInterceptor接口
    3. 重写preHandle方法

    如果通过了拦截器的拦截认证,就会返回true,放行,继续执行之后的代码
    但是如果没有通过,就根据业务的需要,执行响应的代码,并返回false

    @Component  
    public class LoginIntercepter implements HandlerInterceptor {  
        @Override  
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {  
            HttpSession session=request.getSession();  
            if(session!=null&&session.getAttribute("user")!=null)  
                return true;  
            response.sendRedirect("/Blog_Main.html");  
            return false;  
        }  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    2. 将⾃定义拦截器加⼊到系统配置

    1. 定义一个配置类
    2. 使用WebMvcConfigurer类
    3. 重写addInterceptors方法
    4. 将拦截器放入到addInterceptor方法中并指定拦截范围和不拦截范围
      
    @Configuration  
    public class AppConfig implements WebMvcConfigurer {  
        @Autowired  
        LoginIntercepter loginIntercepter;  
      
        @Override  
        public void addInterceptors(InterceptorRegistry registry) {  
    		//加入拦截器
    		registry.addInterceptor(loginIntercepter).  
                    //指定拦截所有的接口
                    addPathPatterns("/**").  
                    //排除不拦截的接口
                    excludePathPatterns("/resources/**").  
                    excludePathPatterns("/**/login").  
                    excludePathPatterns("/**/**.png").  
                    excludePathPatterns("/**/**.jpg").  
                    excludePathPatterns("/Blog_Main.html").  
                    excludePathPatterns("/**/**.css").  
                    excludePathPatterns("/**/**.js");  
        }  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    可以通过多次调用registry.addInterceptor方法来添加多个拦截器

    原理分析

    那Spring拦截器是怎么样做到的呢?

    主要是通过DispatcherServlet来进行实现的.
    这个DispathcherServlet里面的doDispath方法里面主要有一个方法:

    if (!mappedHandler.applyPreHandle(processedRequest, response)) {  
        return;  
    }
    
    • 1
    • 2
    • 3

    这个applyPreHandle方法会处理所有的拦截器方法,下面是它的实现:

    boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {  
        for(int i = 0; i < this.interceptorList.size(); this.interceptorIndex = i++) {  
            HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i);  
            if (!interceptor.preHandle(request, response, this.handler)) {  
                this.triggerAfterCompletion(request, response, (Exception)null);  
                return false;  
            }  
        }  
      
        return true;  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    它会将拦截器列表的所有拦截器都遍历一遍,只要不符合其中任意一个拦截器,就会报错,返回false,拦截不通过.

    添加路径前缀

    还可以在系统配置中将所有的路径都加上前缀.

    1. 创建一个配置类实现WebMvcConfigurer接口
    2. 重写configurePathMatch方法
    3. 第一个参数是新加入的前缀,后一个参数是描述参数的范围
    package com.example.demo.config;  
      
    import org.springframework.beans.factory.annotation.Autowired;  
    import org.springframework.context.annotation.Configuration;  
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;  
    import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;  
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;  
      
      
    @Configuration  
    public class AppConfig implements WebMvcConfigurer {  
        @Autowired  
        LoginIntercepter loginIntercepter;  
        @Override  
        public void configurePathMatch(PathMatchConfigurer configurer) {  
            configurer.addPathPrefix("hhh",c->true);  
        }  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    原来:http://127.0.0.1:8080/user/test
    现在:http://127.0.0.1:8080/hhh/user/test

    加上前缀之后,就必须要带上前缀了

    统一异常处理

    1. 创建一个类
    2. 在这个类前面加上@RestControllerAdvice或@ControllerAdvice注解,两者都可以
    3. 在处理异常的方法前面加上@ExceptionHandler,括号里面写异常的类型
    4. 处理异常的方法的参数是对应的异常类型
    @RestControllerAdvice  
    public class MyExceptionAdvice {  
      
        @ExceptionHandler(Exception.class)    
        public HashMap<String,Object> handleException(Exception e){  
            HashMap<String,Object> hashMap=new HashMap<>();  
            hashMap.put("state",-1);  
            hashMap.put("msg","出现异常");  
            hashMap.put("log",e.getMessage());  
            return hashMap;  
        }  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    统一返回处理

    1. 创建一个类,在类的前面加上@ControllerAdvice注解
    2. 实现ResponseBodyAdvice接口,并是实现它的两个方法
    3. 对于supports方法来说,如果返回true,表示需要进行统一的返回处理.如果返回false,表示不需要统一的返回处理.可以使用这个函数进行过滤
    4. beforeBodyWrite就是对那些supports方法返回true的接口进行处理.一般都是写成前端可以识别的json格式
    @ControllerAdvice  
    public class MyResponseAdvice implements ResponseBodyAdvice {  
      
        @Override  
        public boolean supports(MethodParameter returnType, Class converterType) {  
            if("com.example.demo.controller.UserController".equals(returnType.getMethod().getDeclaringClass().getName()))  
                return true;  
            else  
                return false;  
        }  
      
        @Override  
        public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {  
            HashMap<String, Object> result = new HashMap<>();  
            result.put("success", 1);  
            result.put("message", "");  
            result.put("data", body);  
            return result;  
        }  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    原理分析

    我们发现@ControllerAdvice可以处理异常和返回值.但是这个的原理是什么呢?

    将所有被@ControllerAdvice注解的类方法一个容器中.就是当发生事件的时候,会去调用对应的类的advice方法

  • 相关阅读:
    ctfhub(rce智慧树)
    上海亚商投顾:市场继续缩量调整 汽车、光伏板块领涨
    华为OD机试 - 疫情扩散时间计算 - 矩阵(Java 2024 C卷 200分)
    论文阅读(6)最有效率的后生动物游泳者创造了一堵“虚拟墙”来提高成绩
    ubuntu 22.04 minikube 部署 应用测试
    基于PyTorch3D的GeoAI实现【ESRI】
    Java面试八股之什么是mybatis流式查询
    Linux学习笔记——C程序的编译运行与调试
    四边形不等式
    07数据结构与算法刷题之【树】篇
  • 原文地址:https://blog.csdn.net/weixin_51574797/article/details/126707902