2

什么是AOP

在Spring Boot AOP中,非核心业务功能被定义为切面,核心和非核心功能都开发完成之后,再将两者编织在一起,这就是AOP。

举个例子,假设你的应用程序需要记录每次方法调用的开始时间和结束时间。如果你不使用AOP,你可能需要在每个方法的开头和结尾都写一段代码来记录时间。但是,这样的做法既繁琐又容易遗漏。
使用AOP,你可以创建一个"时间记录"的切面,告诉系统在每个方法执行前后都执行一段代码来记录时间。这样,你就可以把时间记录这个关注点从业务逻辑中剥离出来,让每个方法专注于自己的核心任务,而不必担心时间记录。

AOP中的编程术语和常用注解

切面:非核心业务功能就被定义为切面。比如一个系统的日志功能,它贯穿整个核心业务的逻辑,因此叫做切面
切入点:在哪些类的哪些方法上面切入
通知:在方法执行前/后或者执行前后做什么

前置通知:在被代理方法之前执行
后置通知:在被代理方法之后执行
返回通知:被代理方法正常返回之后执行
异常通知:被代理方法抛出异常时执行
环绕通知:是AOP中强大、灵活的通知,集成前置和后置通知

springboot常见的使用注解

@Pointcut 定义切点
@Before 前置通知
@After 后置通知
@AfterReturning 返回通知
@AfterThrowing 异常通知
@Around 环绕通知

切点常用写法

1)execution(public * (..))——表示匹配所有public方法
2)execution( set(..))——表示所有以“set”开头的方法
3)execution( com.example.controller.TestController.(..))——表示匹配所有TestController接口的方法
4)execution( com.example.controler..(..))——表示匹配controler包下所有的方法
5)execution(* com.example.service...(..))——表示匹配controler包和它的子包下的方法

JoinPoint 对象

JoinPoint对象封装了SpringAop中切面方法的信息,在切面方法中添加JoinPoint参数,就可以获取到封装了该方法信息的JoinPoint对象.

方法名    功能
Signature getSignature();    获取封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class等信息
Object[] getArgs();    获取传入目标方法的参数对象
Object getTarget();    获取被代理的对象
Object getThis();    获取代理对象

ProceedingJoinPoint对象

ProceedingJoinPoint对象是JoinPoint的子接口,该对象只用在@Around的切面方法中, 
Object proceed() throws Throwable //执行目标方法 
Object proceed(Object[] var1) throws Throwable //传入的新的参数去执行目标方法 

使用aop,第一步导入maven dependency

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

编写Controller类

@RestController
@RequestMapping("/test")
public class TestController {

    @GetMapping("hello")
    public String sayHello(){
        System.out.println("hello");
        return "hello";
    }
}

定义切面

@Aspect
@Component
public class TestAspect {

    @Pointcut("execution(* com.example.demo.controller.TestController.sayHello())")
    private void serviceMethods() {}

    // 前置通知,在目标方法执行前执行
    @Before("serviceMethods()")
    public void beforeAdvice(JoinPoint joinPoint) {
        System.out.println("Before executing: " + joinPoint.getSignature().toShortString());
    }

    // 后置通知,在目标方法执行后执行
    @After("serviceMethods()")
    public void afterAdvice(JoinPoint joinPoint) {
        System.out.println("After executing: " + joinPoint.getSignature().toShortString());
    }

    // 返回通知,在目标方法成功返回后执行
    @AfterReturning(pointcut = "serviceMethods()", returning = "result")
    public void afterReturningAdvice(JoinPoint joinPoint, Object result) {
        System.out.println("After returning from: " + joinPoint.getSignature().toShortString());
        System.out.println("Result: " + result);
    }

    // 异常通知,在目标方法抛出异常时执行
    @AfterThrowing(pointcut = "serviceMethods()", throwing = "exception")
    public void afterThrowingAdvice(JoinPoint joinPoint, Exception exception) {
        System.out.println("Exception thrown from: " + joinPoint.getSignature().toShortString());
        System.out.println("Exception: " + exception.getMessage());
    }

    // 环绕通知,在目标方法执行前后都可以插入逻辑
    @Around("serviceMethods()")
    public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("Around - Before executing: " + proceedingJoinPoint.getSignature().toShortString());

        // 执行目标方法
        Object result = proceedingJoinPoint.proceed();

        System.out.println("Around - After executing: " + proceedingJoinPoint.getSignature().toShortString());

        return result;
    }

}

切面类需要打上@Aspect注解表示这是一个切面类,并且加上@Component注解

首先是定义切点,只需定义一个方法,在上面使用@Pointcut注解即可,注解里面内容含义如下:

execution 代表方法被执行时触发
* 代表任意返回值的方法
com.example.demo.controller.TestController.sayHello(..)被织入类的全限定名
(..) 表示任意的参数

发起一个请求

1703321261010_FCCAE851-6021-4d7d-9108-BBBA759F378E.png

通过这个,我们也可以发现各个通知的执行顺序,由于这个案例使用@Around的时候,先执行了一步,之后在执行其他的通知

Around -> Before ->  AfterReturning -> After -> Around

总结

AOP其实使用起来是个很方便的东西,大大降低了相关功能之间的耦合度,使得整个系统井井有条。


kexb
519 声望18 粉丝