• Spring AOP


    1.什么是Spring AOP?

    AOP:面向切面编程,它是一种思想,对某一类事情集中处理。
    OOP:面向对象编程。
    AOP是一种思想,Spring AOP是一个框架,提供了一种对AOP思想的实现。

    2.为什么要用Spring AOP?

    举例:一个系统中,很多页面都需要先验证用户登录,这种情况下,如果采用都写一遍用户登录验证,当功能越来越多,登录验证也越来越多,代码修改和维护成本就比较高了,对于功能统一且使用地方较多的,就可以考虑AOP统一处理。
    AOP可以实现:
    1.统一的用户登录判断;
    2.统一方法执行时间统计;
    3.统一日志记录;
    4.统一的返回格式设置;
    5.统一的异常处理;
    6.事务的开启和提交等;
    AOP可以扩充多个对象的某个能力,AOP可以说是OOP的补充和完善。

    3.AOP的组成

    AOP的组成:
    1.切面:针对与某一个功能的具体定义,某一个功能可能是登录验证功能,也有可能是日志记录功能。
    2.切点:切面中的某一个方法。
    3.连接点:用来匹配切面的多个点。
    4.通知:在AOP的术语当中,切面的工作被称为通知。
    通知可以分为:
    前置通知使用@Before:通知方法会在目标方法调用之前执行;
    后置通知使用@After:通知方法会在目标方法返回或者抛出异常后调用。
    返回之后通知使用@AfterReturning:通知方法会在目标方法返回后调用。
    抛出异常后通知使用@AfterThrowing:通知方法会在目标方法抛出异常后调用。
    环绕通知使用@Around:通知包裹了被通知的方法,在被通知的方法通知之前和调用之后执行自定义的行为。

    4.Spring AOP实现

    Spring AOP实现步骤:
    1.添加Spring AOP框架支持;
    2.定义切面和切点;
    3.定义通知。

    1.添加Spring AOP框架支持;

    在这里插入图片描述

      <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-aop</artifactId>
                <version>2.6.6</version>
            </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2.定义切面和切点

    在一个类上面加注解@Aspect:表明当前类是一个切面
    在这里插入图片描述

    在这里插入图片描述
    切点表达式说明:
    AspectJ支持三种通配符
    1*:匹配任意字符,只匹配一个元素(包、类、方法、参数)
    2 … :匹配任意字符,可以匹配多个元素,在表示类时,必须和*联合使用。
    3 +:表示按照类型匹配指定类的所有类,必须跟在类名后面,如com.cad.Car+,表示继承该类的所有子类包括本身
    切点表达式由切点函数组成,其中execution()是最常用的切点函数,用来匹配方法,语法为:
    execution(<修饰符><返回类型><包.类.方法(参数)><异常>)
    修饰符和异常可以省略

    3.定义通知(前置通知、后置通知、环绕通知)

    在这里插入图片描述

    5.Spring AOP实现原理

    1.Spring AOP是构建在动态代理基础上,因此Spring对AOP的支持局限于方法级别的拦截。
    2.动态代理分为两类:
    1).JDK Porxy:JDK动态代理机制
    2).CGLIB动态代理
    3.代理的生成时机:
    a)编译期;
    b)类加载期;
    c)运行期.

    6.JDK和CGLIB实现的区别

    1.JDK实现,要求被代理类必须实现接口,之后是通过InvocationHandler及Proxy,在运行时动态的在内存中生成了代理类对象,该代理对象是通过实现同样的接口实现,只是在该代理类是在运行期时,动态的织入统一的业务逻辑字节码来完成。
    2.CGLIB实现,被代理类可以不实现接口,通过实现被代理类,在运行时动态的生成代理类对象。

    7.Spring AOP用户统一登录验证的问题

    用户登录拦截器实现:
    1.创建一个拦截器:(判断用户是否登录,实现HandlerInterceptor接口)并重写preHandle
    在这里插入图片描述

    package com.example.demo.config;
    
    import org.springframework.web.servlet.HandlerInterceptor;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    
    /**
     * 登录拦截器
     */
    public class LoginInterretcept implements HandlerInterceptor {
        /**
         * 拦截方法
         */
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            HttpSession session=request.getSession(false);
            if(session!=null && session.getAttribute("userinfo")!=null){
                //已经登录
                return true;
            }
            //401表示没有权限
            response.setStatus(401);
            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

    2.配置拦截器和拦截规则
    在这里插入图片描述

    package com.example.demo.config;
    
    /**
     * 全局配置文件
     */
    
    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 {
        /**
         * 配置拦截器和拦截规则
         * @param registry
         */
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new LoginInterretcept())   //创建一个拦截器
                    .addPathPatterns("/**")    //拦截所有请求
                    .excludePathPatterns("/**/*.html")
                    .excludePathPatterns("/**/*.css")
                    .excludePathPatterns("/**/*.js")
                    .excludePathPatterns("/user/*.login")
                    .excludePathPatterns("/user/reg");
        }
        //设置api统一的访问前缀
        @Override
        public void configurePathMatch(PathMatchConfigurer configurer) {
            configurer.addPathPrefix("api",c->true);
        }
    }
    
    • 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

    8.统一异常处理

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

    1.先给类上添加一个@ControllerAdvice注解,标识当前的类为一个控制器的增强类。
    2.添加具体的异常返回业务代码,并标识为@ExceptionHandler为异常统一处理方法。

    在这里插入图片描述

    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import java.util.HashMap;
    
    /**
     * 统一异常处理
     */
    @ControllerAdvice  //1.标识为控制器增强类
    @ResponseBody
    public class ErrorAdvice {
        //2.添加异常统一处理方法
        @ExceptionHandler(NullPointerException.class)
        public HashMap<String,Object> nullException(NullPointerException e){
            HashMap<String,Object> result=new HashMap<>();
            result.put("succ",200);     //http请求成功了,(大状态)
            result.put("state",-1);     //报错了(业务状态,小状态)
            result.put("message",e.toString());
            return result;
        }
        @ExceptionHandler(Exception.class)
        public HashMap<String,Object> exception(Exception e){
            HashMap<String,Object> result=new HashMap<>();
            result.put("succ",200);
            result.put("state",-1);
            result.put("message",e.toString());
            return result;
        }
    }
    
    • 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

    9.统一数据返回格式

    1.为什么需要统一数据返回格式?
    a)方便前端程序员更好的接收和解析后端数据接口返回的数据;
    b)减低前端程序员和后端程序员的沟通成本,按照某个格式实现就行了,因为所有接口都是这样返回的;
    c)有利于项目统一数据的维护和修改;
    d)有利于后端技术部门的统一规范的标准制定。
    2.统一数据返回格式的实现:@ControllerAdvice+ResponseBodyAdvice的方式实现
    在这里插入图片描述

    package com.example.demo.config;
    
    import org.springframework.core.MethodParameter;
    import org.springframework.http.MediaType;
    import org.springframework.http.server.ServerHttpRequest;
    import org.springframework.http.server.ServerHttpResponse;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
    
    import java.util.HashMap;
    
    /**
     * 统一数据格式处理
     */
    @ControllerAdvice       //1.
    public class ResponseAdvice implements ResponseBodyAdvice {
        //是否要进行内容重写
        @Override
        public boolean supports(MethodParameter returnType, Class converterType) {
            return true;
        }
    
        @Override
        public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
            //统一数据格式的封装
            HashMap<String,Object> result=new HashMap<>();
            result.put("succ",200);     //返回大状态
            result.put("state",1);
            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
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
  • 相关阅读:
    慢速排序算法到底有多慢
    golang 并发--goroutine(四)
    【开源】基于Vue和SpringBoot的服装店库存管理系统
    计算机网络(五)——UDP
    【SSA-BP预测】基于麻雀算法优化BP神经网络回归预测研究(Matlab代码实现)
    SpringBoot3进阶用法
    概括专线接入类型,互联网专线接入方式有哪些?
    KDD‘22推荐系统论文梳理(24篇研究&36篇应用论文)
    Linux计划任务at和cron命令的使用
    mysql基础知识篇(一)
  • 原文地址:https://blog.csdn.net/weixin_51062428/article/details/125499346