Spring AOP

spring AOP基础知识

AOP是什么

AOP是一种变成思想,AOP全名Aspect Orient Programming,直译过来就是面向切面编程。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。AOP简单的一点的理解就是,不改变源代码的情况下,对主功能进行增强或添加新功能。这就很像python中的装饰器一样。

AOP的底层实现

AOP 的底层是通过 Spring 提供的的动态代理技术实现 的。在运行期间,Spring通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,在去调用目标对象的方法,从而完成功能的增强。

AOP的相关术语

  • 通知(Advice): 通知描述了切面何时执行以及如何执行增强处理。
  • 连接点(join point): 类里面哪些方法可以被增强,这些方法就成为连接点
  • 切入点(PointCut): 实际被真正增强的方法(这里是看别人写的有点难理解)。
  • 切面(Aspect): 切面是通知和切点的结合,把通知应用到切入点的过程,是一个动作。

通知又分为五种类型:

  • 前置通知:before
  • 后置通知:afterreturning
  • 环绕通知:around
  • 异常通知:只有发生异常才会通知
  • 最终通知:after无论是否发生异常都会通知

AOP案例

这里使用spring boot来演示

这里通过对原有的订单功能使用AOP进行登录校验,只有登录用户才可以访问订单功能。

准备环境

准备一个ordercontroller

package com.bkhb.springbootaopdemo.controller;

@RestController
@RequestMapping("order")
public class OrderController {

    @GetMapping
    public String listOder() {
        System.out.println("返回订单信息");
        return "返回订单信息";
    }

    @PostMapping
    public String addOder() {
        return "添加订单成功";
    }
}

导入maven依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

创建增强类

@Component
@Aspect // 定义一个切面
public class LoginAdvice {
    // 定义一个切点:OrderController中的所有方法都增强
    @Pointcut(value = "execution(* com.bkhb.springbootaopdemo.controller.OrderController.* (..))")
    private void loginAdvicePointcut(){}

    // 前置通知
    @Before(value = "loginAdvicePointcut()")
    public void before() {
        System.out.println("before...");
    }
    // 后置通知
    @AfterReturning(value = "loginAdvicePointcut()")
    public void afterReturning() {
        System.out.println("afterReturning...");
    }
    // 最终通知
    @After(value = "loginAdvicePointcut()")
    public void after() {
        System.out.println("after...");
    }
    // 异常通知
    @AfterThrowing(value = "loginAdvicePointcut()")
    public void afterThrowing() {
        System.out.println("afterThrowing...");
    }
    // 环绕通知 也可以直接将切入点放在通知的value中
    @Around(value = "execution(* com.bkhb.springbootaopdemo.controller.OrderController.* (..))")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("环绕之前.........");
        //被增强的方法执行
        Object proceed = proceedingJoinPoint.proceed();
        System.out.println("环绕之后.........");
        return proceed;
    }
}

测试一下看看他们的执行顺序

# 正常情况下的打印
环绕之前.........
before...
返回订单信息
afterReturning...
after...
环绕之后.........

取controller中模拟一个异常

@GetMapping
public String listOder() {
    System.out.println("返回订单信息");
    int i = 1 / 0 ;
    return "返回订单信息";
}
# 异常情况下的打印
环绕之前.........
before...
返回订单信息
afterThrowing...
after...

由此可见:

  • 正常情况:环绕通知->前置通知->切入点方法->后置通知->最终通知->环绕通知
  • 异常情况:环绕通知->前置通知->切入点方法->异常通知->最终通知

判断是否登录

这里将认证放入前置通知

改造一下前置通知

@Before(value = "loginAdvicePointcut()")
public void before() {
    System.out.println("before...");
    ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    // 从请求头中获取用户信息
    String user  = sra.getRequest().getHeader("user");
    if (user == null) {
        System.out.println("用户未登录");
        throw new RuntimeException("用户为登录");
    }
    System.out.println(user + "已登录");
}

注:如何是环绕通知,可以直接环绕一个值

测试一下

# 不带用户信息
环绕之前.........
before...
用户未登录
直接报错了

# 带用户信息
环绕之前.........
before...
zhangsan已登录
返回订单信息
afterReturning...
after...
环绕之后.........

切入点表达式

execute表达式

切入点表达式作用:知道对哪个类里面的哪个方法进行增强 
语法结构: execution([权限修饰符] [返回类型(可省略)] [类全路径] [方法名称]([参数列表]) )
例子如下:
    例1:对 com.bkhb.dao.BookDao 类里面的 add 进行增强
		execution(* com.bk.dao.BookDao.add(..))
    例2:对 com.bk.dao.BookDao 类里面的所有的方法进行增强
		execution(* com.bk.dao.BookDao.* (..))
    例3:对 com.atguigu.dao 包里面所有类,类里面所有方法进行增强
		execution(* com.bk.dao.*.* (..))
posted @ 2023-08-02 18:27  冰块好冰  阅读(50)  评论(0)    收藏  举报