• 7天学完Spring:AOP实战,SpringMVC统一处理


    前言

    接下来是 Spring Boot 统一功能处理模块了,也是 AOP 的实战环节,要实现的课程目标有以下 3 个:

    1. 统一用户登录权限验证;
    2. 统一数据格式返回;
    3. 统一异常处理。

    一丶用户登录权限效验

    用户登录权限的发展从之前每个方法中自己验证用户登录权限,到现在统一的用户登录验证处理,它是 一个逐渐完善和逐渐优化的过程。

    <1>最初用户登录验证

    我们先来回顾一下最初用户登录验证的实现方法:

    @RestController
    @RequestMapping("/user")
    public class UserController {
     /**
         * 某方法 1
         */
        @RequestMapping("/m1")
        public Object method(HttpServletRequest request) {
            // 有 session 就获取,没有不会创建
            HttpSession session = request.getSession(false);
            if (session != null && session.getAttribute("userinfo") != null) {
                // 说明已经登录,业务处理
                return true;
           } else {
                // 未登录
                return false;
           }
       }
        /**
         * 某方法 2
         */
        @RequestMapping("/m2")
        public Object method2(HttpServletRequest request) {
            // 有 session 就获取,没有不会创建
            HttpSession session = request.getSession(false);
            if (session != null && session.getAttribute("userinfo") != null) {
                // 说明已经登录,业务处理
                return true;
           } else {
                // 未登录
                return false;
           }
       }
        // 其他方法...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    这是我们刚开始的用法,就是刚刚学习完SpringBoot时候的用法,用Session来判断当前的登录状态。但是问题是什么呢?
    1. 每个方法中都要单独写用户登录验证的方法,即使封装成公共方法,也一样要传参调用和在方法中进行判断。
    2. 添加控制器越多,调用用户登录验证的方法也越多,这样就增加了后期的修改成本和维护成本。
    3. 这些用户登录验证的方法和接下来要实现的业务几何没有任何关联,但每个方法中都要写一遍。

    所以就很麻烦,但是小伙伴们灵机一动,诶,上节课不是学了AOP吗?刚才切面编程是处理这个问题的好手呀

    <2>Spring AOP 用户统一登录验证的问题

    竟我们上节课刚学,但是这里有一个问题

    然后就又有小伙伴说了,我们可以这么写呀

    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.springframework.stereotype.Component;
    @Aspect
    @Component
    public class UserAspect {
        // 定义切点方法 controller 包下、子孙包下所有类的所有方法
        @Pointcut("execution(* com.example.demo.controller..*.*(..))")
        public void pointcut(){ }
        // 前置方法
        @Before("pointcut()")
        public void doBefore(){
           
       }
        
        // 环绕方法
        @Around("pointcut()")
        public Object doAround(ProceedingJoinPoint joinPoint){
            Object obj = null;
            System.out.println("Around 方法开始执行");
            try {
                // 执行拦截方法
               obj = joinPoint.proceed();
           } catch (Throwable throwable) {
                throwable.printStackTrace();
           }
            System.out.println("Around 方法结束执行");
            return obj;
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    对,没错,你是判断了是否登录,但是,是有问题的!在这里插入图片描述

    <3>拦截器

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

    1. 创建自定义拦截器,实现 HandlerInterceptor 接口的 preHandle(执行具体方法之前的预处理)方法。
    2. 将自定义拦截器加入 WebMvcConfigurer 的 addInterceptors 方法中。

    如下
    在这里插入图片描述

    拦截功能演示

    首先,我们原本的代码是没有登录验证的,这里先随便写一个登录功能
    在这里插入图片描述
    在这里插入图片描述
    然后开始书写我们的拦截器
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    嗯?怎么回事?怎么回事空呢?这没有道理,但是仔细想想,我们当前的代码,是在AOP基础上书写的,然后前面的AOP代码有没有什么问题,仔细一想,好像有点!
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    <4>拦截器原理

    所有的 Controller 执行都会通过一个调度器 DispatcherServlet 来实现,这一点可以从 Spring Boot 控制台的打印信息看出,如下图所示:
    在这里插入图片描述
    而所有方法都会执行 DispatcherServlet 中的 doDispatch 调度方法。
    执行 Controller 之前,会先调用 预处理方法 applyPreHandle,而applyPreHandle 方法的实现源码如下:

    boolean applyPreHandle(HttpServletRequest request, HttpServletResponse
    response) throws Exception {
        for(int i = 0; i < this.interceptorList.size(); this.interceptorIndex =
    i++) {
            // 获取项目中使用的拦截器 HandlerInterceptor
            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
    • 12
    • 13
    • 14

    从上述源码可以看出,在 applyPreHandle 中会获取所有的拦截器 HandlerInterceptor 并执行拦截器中
    的 preHandle 方法,这样就会咱们前面定义的拦截器对应上了,

    <5>统一访问前缀添加

    这个是个挺简单的语法
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    那可能会有铁子问了,你这么搞的意义在哪里呢?还特意加个访问前缀
    在这里插入图片描述

    二丶统一异常处理

    统一异常处理使用的是 @ControllerAdvice + @ExceptionHandler 来实现的,@ControllerAdvice 表示控制器通知类,@ExceptionHandler 是异常处理器,两个结合表示当出现异常的时候执行某个通知,也就是执行某个方法事件
    此时我们给个异常事件
    在这里插入图片描述
    这个时候应该会有一个异常信息
    在这里插入图片描述
    但是此时,就很难受不是嘛?我们换种方法来写
    在这里插入图片描述
    这种也可以,可是就很不现实不是嘛?一个错误来一个异常?每个错误都给你加一个?
    在这里插入图片描述
    看我下面的写法,当前类加上@ControllerAdvice注解
    在这里插入图片描述
    在这里插入图片描述此时打印信息如下:
    在这里插入图片描述
    在这里插入图片描述

    <1>重改1/0异常

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    <2>重改登录异常

    所以这里修改一下登录报错的信息
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    <3>写法小梳理

    在这里插入图片描述

    在这里插入图片描述

    三丶统一数据格式封装

    <1>为什么需要统一的数据格式封装呢?

    统一数据返回格式的优点有很多,比如以下几个:
    1. 方便前端程序员更好的接收和解析后端数据接口返回的数据。
    2. 降低前端程序员和后端程序员的沟通成本,按照某个格式实现就行了,因为所有接口都是这样返回
    的。
    3. 有利于项目统一数据的维护和修改。
    4. 有利于后端技术部门的统一规范的标准制定,不会出现稀奇古怪的返回内容。


    如果感觉有点迷的话,那就不看上面这个,看我下面的图示
    在这里插入图片描述

    <2>怎么实现呢?

    这个不难,看我下面图示的步骤
    在这里插入图片描述
    然后之后返回的内容如下
    在这里插入图片描述
    然后到了这里,就讲完啦,不对,还有个问题要忘了说了
    在这里插入图片描述
    就是返回值不能是你要统一返回的不能是null值嗷

  • 相关阅读:
    图像超分经典网络ESRGAN精确解析
    22noip10连 day7--考后总结
    C6657 GPIO16~31中断配置
    【C++ techniques】Reference counting(引用计数)
    Kubernetes(k8s)的Pod控制器Deployment详细讲解
    rm: cannot remove `.user.ini‘: Operation not permitted异常该如何解决?
    svn入门到精通
    DVWA 靶场 Open HTTP Redirect 通关解析
    找到页面当前元素z-index最高的数值
    3900页手册415集视频426G资料迅为RK3568开发板
  • 原文地址:https://blog.csdn.net/weixin_53860901/article/details/126002975