0


Spring AOP 入门、实现原理及实践案例

  Spring 是目前 Java 领域最为流行的开发框架之一,它提供了很多方便快捷的功能,其中之一就是 AOP(Aspect Oriented Programming),即面向切面编程。本文将详细介绍 Spring AOP 的实现原理、核心概念以及在实际应用中的使用案例。

1. AOP 的基本概念

1.1 切入点(Pointcut)

  切入点是一个表达式,用于描述哪些方法将被拦截执行,通常使用正则表达式或通配符来匹配方法名或类名。Spring AOP 支持两种类型的切入点:静态切入点和动态切入点。静态切入点在创建时就已经确定,而动态切入点则需要在运行时根据实际情况进行计算。

1.2 通知(Advice)

  通知是指 AOP 在拦截到被选定的方法后,所执行的代码块。Spring AOP 中支持五种不同类型的通知:

  • 前置通知(Before Advice):在目标方法执行之前执行。
  • 后置通知(After Returning Advice):在目标方法正常完成后执行。
  • 异常通知(After Throwing Advice):在目标方法抛出异常时执行。
  • 最终通知(After Finally Advice):在目标方法完成之后执行,无论是否发生异常。
  • 环绕通知(Around Advice):覆盖目标方法的执行,需要手动调用目标方法。

1.3 切面(Aspect)

  切面是将切入点和通知组合在一起的实体对象,用于描述哪些方法应该在何时被拦截,并指定要执行的通知代码块。

2. Spring AOP 的实现原理

  Spring AOP 的实现原理是基于 JDK 动态代理或 CGLIB 字节码技术。在目标对象上创建一个动态代理或子类,拦截所需要的方法并执行对应的通知,在执行完毕后再将控制权转交给目标对象。这样就可以在不修改目标对象代码的情况下,实现对目标方法的增强。

  具体来说,Spring AOP 拦截方法的实现分为以下几个步骤:

  1. 定义切面,配置切入点和通知。
  2. 根据目标对象类型创建代理对象。
  3. 拦截符合切入点要求的方法,并执行所配置的通知代码块。

3. Spring AOP 实践案例

  为了更好地理解 Spring AOP 的实现原理,下面以一个简单的实践案例为例:使用 AOP 打印每个方法执行的日志,日志内容包括方法执行、执行结果、异常信息等方面。

3.1 定义切入点和通知

  首先,需要定义一个切入点,这里为了方便,直接匹配所有 public 方法:

@Pointcut("execution(public * *(..))")publicvoidlogPointCut(){}

  然后,定义一个环绕通知,在目标方法执行前后打印日志:

@Around("logPointCut()")publicObjectaround(ProceedingJoinPoint joinPoint)throwsThrowable{long startTime =System.currentTimeMillis();Object result = joinPoint.proceed();long endTime =System.currentTimeMillis();long duration = endTime - startTime;String methodName = joinPoint.getSignature().getName();String className = joinPoint.getTarget().getClass().getSimpleName();String args =Arrays.toString(joinPoint.getArgs());if(result instanceofSerializable){
        log.info("{}#{}({}, {}) : {} , 耗时 {} ms", className, methodName, args, result, duration);}else{
        log.info("{}#{}({}, [not serializable object]) , 耗时 {} ms", className, methodName, args, duration);}return result;}

  上述代码中,

@Around

注解表示该方法是一个环绕通知,并且会拦截名为

logPointCut()

的切入点所匹配到的所有方法。

ProceedingJoinPoint

类型表示需要拦截的连接点,通过调用其

proceed()

方法可以继续执行原本要执行的目标方法,返回值类型为 Object,即目标方法的执行结果。

3.2 配置 AOP

  接下来需要在 Spring 配置文件中配置 AOP,将切面和切入点以及通知绑定在一起:

<!-- 定义切面 --><beanid="logAspect"class="com.example.LogAspect"/><!-- 配置 AOP --><aop:config><aop:aspectref="logAspect"><!-- 声明切入点 --><aop:pointcutid="logPointCut"expression="execution(public * *(..))"/><!-- 声明通知 --><aop:aroundmethod="around"pointcut-ref="logPointCut"/></aop:aspect></aop:config>

  上述代码中,

<bean>

标签定义了一个名为

logAspect

的 JavaBean 对象,并指定其类为

com.example.LogAspect

,即定义了切面对象。而

<aop:config>

标签则表示开始 AOP 配置,其中

<aop:aspect>

定义了一个切面,其

ref

属性指向

logAspect

,即引用前面定义的切面对象;

<aop:pointcut>

声明了一个切入点,其

expression

属性使用与前面定义的一致;

<aop:around>

则声明了一个环绕通知,将其方法名指定为

around

,并将其与前面定义的切入点绑定在一起。

3.4 SpringBoot版本的AOP案例

注意:在 Spring Boot 中使用 AOP 需要在启动类的配置中添加 @EnableAspectJAutoProxy 注解来开启自动代理功能,以便能够使用 AOP 切面。

@Aspect@ComponentpublicclassLogAspect{@Pointcut("execution(public * *(..))")publicvoidlogPointCut(){}@Around("logPointCut()")publicObjectaround(ProceedingJoinPoint joinPoint)throwsThrowable{long startTime =System.currentTimeMillis();Object result = joinPoint.proceed();long endTime =System.currentTimeMillis();long duration = endTime - startTime;String methodName = joinPoint.getSignature().getName();String className = joinPoint.getTarget().getClass().getSimpleName();String args =Arrays.toString(joinPoint.getArgs());if(result instanceofSerializable){
            log.info("{}#{}({}, {}) : {} , 耗时 {} ms", className, methodName, args, result, duration);}else{
            log.info("{}#{}({}, [not serializable object]) , 耗时 {} ms", className, methodName, args, duration);}return result;}}

3.5 测试应用

  最后,在业务代码中添加一些方法并测试:

@ServicepublicclassMyService{publicvoidsayHello(String name){System.out.println("Hello, "+ name +"!");}publicintdivide(int a,int b){return a / b;}}

  上述代码定义了一个名为

MyService

的服务类,其中包含了两个简单的方法,分别是打印问候语和除法运算。现在,只需要在 Spring 容器中注入该服务类,并调用其方法即可,AOP 将会在控制台打印出对应的日志信息:

MyService myService = context.getBean(MyService.class);
myService.sayHello("World");
myService.divide(10,2);
myService.divide(10,0);

  其中第一行代码是从 Spring 容器中获取该服务类的实例对象,然后依次调用

sayHello

divide

方法,可以看到控制台输出了类似下面的日志信息:

com.example.MyService#sayHello([World], null) : null , 耗时 0 ms
com.example.MyService#divide([10, 2], 5) : 2 , 耗时 0 ms
com.example.MyService#divide([10, 0], java.lang.ArithmeticException: / by zero) , 耗时 1 ms

4. 总结

  Spring AOP 是一种很方便实用的面向切面编程技术,通过代理或字节码技术实现对目标方法的拦截和通知,从而实现对目标方法的增强。在实际应用中,通常使用切入点、通知和切面来描述 AOP 的核心概念,并通过配置文件将它们组合在一起来实现具体功能。

标签: spring java 后端

本文转载自: https://blog.csdn.net/java_cpp_/article/details/130888148
版权归原作者 JavaWizard-M 所有, 如有侵权,请联系我们删除。

“Spring AOP 入门、实现原理及实践案例”的评论:

还没有评论