Spring-AOP

Spring-AOP

AOP:Aspect Oriented Programming(面向切面编程)

OOP:Object Oriented Programming(面向对象编程)

在 Spring 框架中,AOP(面向切面编程,Aspect-Oriented Programming)是一种编程范式,它通过将关注点(如事务管理、日志记录、安全等)从业务逻辑中分离出来,使得代码更加模块化和可维护。AOP 允许你将横切关注点(cross-cutting concerns)封装在单独的单元中,这样可以减少代码重复并增强系统的可重用性。

静态代理

静态代理是在编译时就确定了代理类和被代理类的关系。代理类实现与被代理类相同的接口,并在代理类中持有被代理类的引用。静态代理的优点是简单易懂,但缺点是每增加一个被代理类,就需要增加一个代理类,导致代码重复。

// 被代理的接口  
interface Subject {  
    void request();  
}  

// 被代理的类  
class RealSubject implements Subject {  
    @Override  
    public void request() {  
        System.out.println("RealSubject: Handling request.");  
    }  
}  

// 代理类  
class ProxySubject implements Subject {  
    private RealSubject realSubject;  

    public ProxySubject() {  
        this.realSubject = new RealSubject();  
    }  

    @Override  
    public void request() {  
        System.out.println("ProxySubject: Pre-processing request.");  
        realSubject.request();  
        System.out.println("ProxySubject: Post-processing request.");  
    }  
}  

// 测试  
public class StaticProxyTest {  
    public static void main(String[] args) {  
        Subject proxy = new ProxySubject();  
        proxy.request();  
    }  
}

动态代理

动态代理是在运行时创建代理类,Java提供了java.lang.reflect.Proxy类和InvocationHandler接口来实现动态代理。动态代理的优点是可以在运行时决定代理的对象,减少了代码的重复。

强制要求,目标对象必有接口。代理的也只是接口规定的方法。

import java.lang.reflect.InvocationHandler;  
import java.lang.reflect.Method;  
import java.lang.reflect.Proxy;  

// 被代理的接口  
interface Subject {  
    void request();  
}  

// 被代理的类  
class RealSubject implements Subject {  
    @Override  
    public void request() {  
        System.out.println("RealSubject: Handling request.");  
    }  
}  
// 动态代理处理器  
class DynamicProxyHandler implements InvocationHandler {  
    private Object target;  

    public DynamicProxyHandler(Object target) {  
        this.target = target;  
    }  

    @Override  
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
        System.out.println("DynamicProxy: Pre-processing request.");  
        Object result = method.invoke(target, args);  
        System.out.println("DynamicProxy: Post-processing request.");  
        return result;  
    }  
}  

// 测试  
public class DynamicProxyTest {  
    public static void main(String[] args) {  
        RealSubject realSubject = new RealSubject();  
        Subject proxy = (Subject) Proxy.newProxyInstance(  
                realSubject.getClass().getClassLoader(),  
                new Class[]{Subject.class},  
                new DynamicProxyHandler(realSubject)  
        );  
        proxy.request();  
    }  
}

AOP

AOP(面向切面编程)是一种编程范式,它允许将横切关注点(如日志记录、事务管理、安全等)从业务逻辑中分离出来。

AOP思想主要的应用场景

AOP(面向切面编程)是一种编程范式,它通过将通用的横切关注点(如日志、事务、权限控制等)与业务逻辑分离,使得代码更加清晰、简洁、易于维护。AOP可以应用于各种场景,以下是一些常见的AOP应用场景:

  1. 日志记录:在系统中记录日志是非常重要的,可以使用AOP来实现日志记录的功能,可以在方法执行前、执行后或异常抛出时记录日志。
  2. 事务处理:在数据库操作中使用事务可以保证数据的一致性,可以使用AOP来实现事务处理的功能,可以在方法开始前开启事务,在方法执行完毕后提交或回滚事务。
  3. 安全控制:在系统中包含某些需要安全控制的操作,如登录、修改密码、授权等,可以使用AOP来实现安全控制的功能。可以在方法执行前进行权限判断,如果用户没有权限,则抛出异常或转向到错误页面,以防止未经授权的访问。
  4. 性能监控:在系统运行过程中,有时需要对某些方法的性能进行监控,以找到系统的瓶颈并进行优化。可以使用AOP来实现性能监控的功能,可以在方法执行前记录时间戳,在方法执行完毕后计算方法执行时间并输出到日志中。
  5. 异常处理:系统中可能出现各种异常情况,如空指针异常、数据库连接异常等,可以使用AOP来实现异常处理的功能,在方法执行过程中,如果出现异常,则进行异常处理(如记录日志、发送邮件等)。
  6. 缓存控制:在系统中有些数据可以缓存起来以提高访问速度,可以使用AOP来实现缓存控制的功能,可以在方法执行前查询缓存中是否有数据,如果有则返回,否则执行方法并将方法返回值存入缓存中。
  7. 动态代理:AOP的实现方式之一是通过动态代理,可以代理某个类的所有方法,用于实现各种功能。
    综上所述,AOP可以应用于各种场景,它的作用是将通用的横切关注点与业务逻辑分离,使得代码更加清晰、简洁、易于维护。

Spring AOP底层技术组成

动态代理(InvocationHandler):JDK原生的实现方式,需要被代理的目标类必须实现接口。因为这个技术要求代理对象和目标对象实现同样的接口(兄弟两个拜把子模式)。

cglib:通过继承被代理的目标类(认干爹模式)实现代理,所以不需要目标类实现接口。

AspectJ:早期的AOP实现的框架,SpringAOP借用了AspectJ中的AOP注解。

AOP的基本概念

切面(Aspect)

切面是横切关注点的模块化,它可以被看作是一个类,该类包含相关的切点通知(Advice)。

切点(Pointcut)

切点是一种表达式,用于定义在哪些连接点执行通知。连接点是程序执行期间的某个点,例如方法调用。

通知(Advice)

通知是在切点处执行的动作,AOP有几种类型的通知:

  • 前置通知(@Before):目标方法执行前执行。
  • 后置通知(@After):目标方法执行后执行,且无论方法是否抛出异常。
  • 返回通知(@AfterReturning):目标方法成功返回后执行。
  • 异常通知(@AfterThrowing):目标方法抛出异常后执行。
  • 环绕通知(@Around):可以在方法执行前后自定义逻辑,能够控制方法是否被调用。

连接点(Joinpoint)

连接点是程序执行中的一个点,这个点可以是方法调用、对象实例化、异常处理等。

Spring AOP 示例

Maven 依赖

首先,确保你的 pom.xml 中包含 Spring AOP 和 AspectJ 的依赖:

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

创建一个切面

接下来,创建一个切面(Aspect),用于定义横切关注点。

 /*
 通知方法
 @Before:方法执行之前运行。
 @AfterReturning:方法执行正常返回结果运行。
 @AfterThrowing:方法抛出异常运行。
 @After:方法执行之后运行

切入点表达式:【指定要进行横切的类】
execution(方法的全签名):

全写法:[public] int [com.atguigu.spring.aop.calculator.MathCalculator.] add(int,int) [throwsArithmeticException]
省略写法:int add(int,int)
通配符:
	*  :表示任意字符
	.. :代表多个参数
	.. :代表多层包
 */

@Before("execution(int *(int,int))")
public void logStart(JoinPoint joinPoint){
    //joinPoint=>这个点可以是方法调用、对象实例化、异常处理等
   System.out.println("【切面 - 日志】开始...");
}

@After("execution(int *(int,int))")
public void logEnd(JoinPoint joinPoint){
   //joinPoint=>这个点可以是方法调用、对象实例化、异常处理等
  System.out.println("【切面 - 日志】结束...");
}


@AfterReturning("execution(int *(int,int))")
public void logReturn(JoinPoint joinPoint){
     //joinPoint=>这个点可以是方法调用、对象实例化、异常处理等
    System.out.println("【切面 - 日志】返回...");
}


@AfterThrowing("execution(int *(int,int))")
public void logException(JoinPoint joinPoint){
    //joinPoint=>这个点可以是方法调用、对象实例化、异常处理等
   System.out.println("【切面 - 日志】异常...");
}

创建业务类

创建一个业务类,用于测试 AOP 功能。

//接口
public interface ArithmeticTest {
     int add(int i ,int j);
     int sub(int i ,int j);
     int mul(int i ,int j);
     int div(int i ,int j);
}

//实现类
@Component
public class ArithmeticTestImpl implements ArithmeticTest {
    @Override
    public int add(int i, int j) {
        return i+j;
    }
    @Override
    public int sub(int i, int j) {
        return i-j;
    }

    @Override
    public int mul(int i, int j) {
        return i*j;
    }

    @Override
    public int div(int i, int j) {
        return i/j;
    }
}

@SpringBootTest测试

@SpringBootTest
public class AopTest {
    @Autowired
    private ArithmeticTest arithmeticTest;
    @Test
    public void test01(){
        arithmeticTest.add(1,3);
    }
}

通知方法的执行顺序

正常:前置通知 ==》目标方法 ==》返回通知 ==》后置通知

异常:前置通知 ==》目标方法 ==》异常通知 ==》后置通知

切面表达式

execution 最常用;匹配方法执行连接点,可以匹配方法、类、包。

表达式模式:

execution(modifier? ret-type declaring-type?name-pattern(param-pattern) throws-pattern?)	
表达式解释:
modifier:匹配修饰符,public, private 等,省略时匹配任意修饰符

ret-type:匹配返回类型,使用 * 匹配任意类型

declaring-type:匹配目标类,省略时匹配任意类型

.. 匹配包及其子包的所有类
name-pattern:匹配方法名称,使用 * 表示通配符

* 匹配任意方法
set* 匹配名称以 set 开头的方法

param-pattern:匹配参数类型和数量
() 匹配没有参数的方法
(..) 匹配有任意数量参数的方法
(*) 匹配有一个任意类型参数的方法
(*,String) 匹配有两个参数的方法,并且第一个为任意类型,第二个为 String 类型
throws-pattern:匹配抛出异常类型,省略时匹配任意类型
// 匹配public方法
execution(public * *(..))

// 匹配名称以set开头的方法
execution(* set*(..))

// 匹配AccountService接口或类的方法
execution(* com.xyz.service.AccountService.*(..))

// 匹配service包及其子包的类或接口
execution(* com.xyz.service..*(..))
    

within匹配指定类型。匹配指定类的任意方法,不能匹配接口。

// 匹配service包的类
within(com.xyz.service.*)

// 匹配service包及其子包的类
within(com.xyz.service..*)

// 匹配AccountServiceImpl类
within(com.xyz.service.AccountServiceImpl)

args匹配方法参数类型和数量,参数类型可以为指定类型及其子类。

使用 execution 表达式匹配参数时,不能匹配参数类型为子类的方法。

// 匹配参数只有一个且为Serializable类型(或实现Serializable接口的类)
args(java.io.Serializable)

// 匹配参数个数至少有一个且为第一个为Example类型(或实现Example接口的类)
args(cn.codeartist.spring.aop.pointcut.Example,..)

@annotation匹配方法是否含有注解。当方法上使用了注解,该方法会被匹配,在接口方法上使用注解不匹配。

// 匹配使用了Demo注解的方法
@annotation(cn.codeartist.spring.aop.pointcut.Demo)

要使切点的匹配性能达到最佳,编写表达式时,应该尽可能缩小匹配范围,切点表达式分为三大类:

  • 类型表达式:匹配某个特定切入点,如 execution

  • 作用域表达式:匹配某组切入点,如 within

  • 上下文表达式:基于上下文匹配某些切入点,如 this、target 和 @annotation

切点表达式组合

使用 &&||! 来组合多个切点表达式,表示多个表达式“与”、“或”和“非”的逻辑关系。
这可以用来组合多种类型的表达式,来提升匹配效率。

// 匹配doExecution()切点表达式并且参数第一个为Account类型的方法
@Before("doExecution() && args(account,..)")
public void validateAccount(Account account) {
    // 自定义逻辑
}

类型 语法 解释&案例
类型 语法**** 解释&案例
execution execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?) 最常用;匹配方法执行连接点 如:execution(* com.example.service..(..)) 匹配com.example.service包及其子包中所有类的所有方法。
within within(type-pattern) 匹配指定类型内(包括子类型)的所有连接点 与execution表达式类似,但更侧重于类型而非具体的方法签名
this this(type-pattern) 匹配代理对象是指定类型或其子类型的任何连接点
target target(type-pattern) 匹配目标对象是指定类型或其子类型的任何连接点 如:target(com.example.MyBean) 匹配所有目标对象是MyBean类型或其子类型的连接点。
args args(param-pattern) 匹配方法参数是指定类型或其子类型的任何连接点 如:args(java.io.Serializable);匹配所有参数是序列化接口的方法
bean bean(bean-id-or-name-pattern) 匹配特定Spring bean的所有连接点。这依赖于Spring bean的名称或ID。
@target @target(annotation-type) 匹配标注了指定注解的所有目标对象的方法。 如@target(org.springframework.transaction.annotation.Transactional)
@args @args(annotation-type) 匹配方法参数标注指定注解。如:@args(com.atguigu.Hello); 匹配参数上标注@Hello注解的
@within @within(annotation-type) 匹配目标对象类型上拥有指定注解的所有方法。如: @within(org.springframework.transaction.annotation.Transactional) 匹配所有目标对象类上被@Transactional注解标注
@annotation @annotation(annotation-type) 匹配任何被指定注解标注的方法。如,@annotation(org.springframework.transaction.annotation.Transactional) 匹配所有被@Transactional注解标注的方法。
组合表达式 &&、||、! &&代表同时成立、||代表某个成立、!代表非(某个不成立)

公共配置@Pointcut

@Aspect
@Component
public class DemoAspect {
    
    //配置公共的切面表单时
    @Pointcut("execution(切面表达式)")
    private void pointcut() {}
    
    //调用公共的切面表达式
    @Before("pointcut()")
    public void doBefore(JoinPoint joinPoint) {
        // 自定义逻辑
    }
}

环绕通知

环绕通知对应整个 try...catch...finally 结构,包括前面四种通知的所有功能。

// 使用@Around注解标明环绕通知方法
@Around(value = "com.atguigu.aop.aspect.AtguiguPointCut.transactionPointCut()")
public Object manageTransaction(
    
        // 通过在通知方法形参位置声明ProceedingJoinPoint类型的形参,
        // Spring会将这个类型的对象传给我们
        ProceedingJoinPoint joinPoint) {
    
    // 通过ProceedingJoinPoint对象获取外界调用目标方法时传入的实参数组
    Object[] args = joinPoint.getArgs();
    
    // 通过ProceedingJoinPoint对象获取目标方法的签名对象
    Signature signature = joinPoint.getSignature();
    
    // 通过签名对象获取目标方法的方法名
    String methodName = signature.getName();
    
    // 声明变量用来存储目标方法的返回值
    Object targetMethodReturnValue = null;
    
    try {
    
        // 在目标方法执行前:开启事务(模拟)
        log.debug("[AOP 环绕通知] 开启事务,方法名:" + methodName + ",参数列表:" + Arrays.asList(args));
    
        // 过ProceedingJoinPoint对象调用目标方法
        // 目标方法的返回值一定要返回给外界调用者
        targetMethodReturnValue = joinPoint.proceed(args);
    
        // 在目标方法成功返回后:提交事务(模拟)
        log.debug("[AOP 环绕通知] 提交事务,方法名:" + methodName + ",方法返回值:" + targetMethodReturnValue);
    
    }catch (Throwable e){
    
        // 在目标方法抛异常后:回滚事务(模拟)
        log.debug("[AOP 环绕通知] 回滚事务,方法名:" + methodName + ",异常:" + e.getClass().getName());
    
    }finally {
    
        // 在目标方法最终结束后:释放数据库连接
        log.debug("[AOP 环绕通知] 释放数据库连接,方法名:" + methodName);
    
    }
    
    return targetMethodReturnValue;
}

注意:目标方法抛异常后,处理完一定要向上抛出异常。保证链路完整。

ProceedingJoinPoint

说明:使用 org.aspectj.lang.ProceedingJoinPoint 允许你在环绕通知中控制方法的执行。

用法:你可以使用此参数在通知中进行方法的执行和参数传递。

import org.aspectj.lang.ProceedingJoinPoint;  
import org.aspectj.lang.annotation.Around;  
import org.aspectj.lang.annotation.Aspect;  
import org.springframework.stereotype.Component;  

@Aspect  
@Component  
public class TimingAspect {  
    //任意返回值类型   在com.example.service包下的  任意类名 任意方法名 (任意类型一个或多个形参)
    @Around("execution(* com.example.service.*.*(..))")  
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {  
        long start = System.currentTimeMillis();  
        Object proceed = joinPoint.proceed(); // 执行目标方法  
        long executionTime = System.currentTimeMillis() - start;  

        System.out.println("Method executed in: " + executionTime + "ms");  
        return proceed;  
    }  
}

连接点(Joinpoint)

连接点(Joinpoint)是面向切面编程(AOP)中的一个重要概念,指的是程序执行过程中可以插入横切关注点(如通知)的特定点。连接点可以是多个不同的事件,例如方法调用、对象实例化、字段的访问、异常处理等。

getSignature():返回连接点的签名信息(即方法名、参数类型等)。

JoinPoint joinPoint;
Signature signature = joinPoint.getSignature();  
System.out.println("Method Name: " + signature.getName());  

getTarget():返回连接点所对应的目标对象,通常是正在被代理的对象。

Object target = joinPoint.getTarget();  

getArgs():返回连接点方法的参数,返回类型为 Object[]

Object[] args = joinPoint.getArgs();  
for (Object arg : args) {  
    System.out.println(arg);  
}  

getThis():返回当前代理对象,通常是进行切面处理的对象。

Object proxy = joinPoint.getThis();  

getStaticPart(): 返回连接点的静态部分,通常用于获取与该连接点的相关信息。

Joinpoint staticPart = joinPoint.getStaticPart();  

方法参数

说明:你还可以直接在切面方法中定义与切点方法参数类型相匹配的参数,以获取原始参数值。

用法:在切点表达式中,匹配的方法参数会对应到切面方法的参数。

import org.aspectj.lang.annotation.Aspect;  
import org.aspectj.lang.annotation.Before;  
import org.springframework.stereotype.Component;  

@Aspect  
@Component  
public class UserServiceAspect {  
    @Before("execution(* com.example.service.UserService.createUser(..)) && args(user)")  
    public void beforeCreateUser(User user) {  
        System.out.println("Creating user: " + user.getName());  
    }  
}

其他类型

Signature:你可以通过参数获得连接点的方法签名。

import org.aspectj.lang.Signature;  
@Before("execution(* com.example.service.*.*(..))")  
public void logMethodName(Signature signature) {  
    System.out.println("Method called: " + signature.getName());  
}  

多切面优先级设置

相同目标方法上同时存在多个切面时,切面的优先级控制切面的内外嵌套顺序。

  • 优先级高的切面:外面
  • 优先级低的切面:里面

使用 @Order 注解可以控制切面的优先级:

  • @Order(较小的数):优先级高
  • @Order(较大的数):优先级低

image-20240901161249147

import org.aspectj.lang.annotation.Aspect;  
import org.aspectj.lang.annotation.Before;  
import org.springframework.core.annotation.Order;  
import org.springframework.stereotype.Component;  

@Aspect  
@Component  
@Order(1) // 高优先级的切面  
public class FirstAspect {  

    @Before("execution(* com.example.service.*.*(..))")  
    public void beforeFirst() {  
        System.out.println("FirstAspect: Before method execution");  
    }  
}  

@Aspect  
@Component  
@Order(2) // 低优先级的切面  
public class SecondAspect {  

    @Before("execution(* com.example.service.*.*(..))")  
    public void beforeSecond() {  
        System.out.println("SecondAspect: Before method execution");  
    }  
}

扩展(@EnableAspectJAutoProxy)

@EnableAspectJAutoProxy 是 Spring Framework 中的一个注解,用于启用 AspectJ 风格的切面编程(AOP)功能。通过该注解,Spring 可以自动识别和支持用 @Aspect 注解标记的切面,并创建所需的代理对象来管理它们。

主要功能:

  • 启用 AOP 的支持:使用 @EnableAspectJAutoProxy 后,Spring 将能够处理切面和对应的切入点,支持用 @Before@After@Around 等注解定义的通知方法。
  • 代理创建:当使用该注解后,Spring 会为被标注为切面的 beans 创建代理对象。这些代理对象能够在方法执行前后执行切面逻辑。
  • 配置方式:通常将此注解放在一个配置类上(该类使用 @Configuration 注解标识),告知 Spring 应该启用 AOP 功能并扫描切面。
  • 代理类型配置:你可以通过设置 proxyTargetClass 属性为 truefalse,来选择使用 JDK 动态代理还是 CGLIB 代理。
import org.aspectj.lang.annotation.Aspect;  
import org.aspectj.lang.annotation.Before;  
import org.springframework.context.annotation.Bean;  
import org.springframework.context.annotation.Configuration;  
import org.springframework.context.annotation.EnableAspectJAutoProxy;  

@Configuration  
@EnableAspectJAutoProxy  
public class AppConfig {  

    @Bean  
    public MyAspect myAspect() {  
        return new MyAspect();  
    }  
}  

@Aspect  
class MyAspect {  
    
    @Before("execution(* com.example.service.*.*(..))")  
    public void beforeAdvice() {  
        System.out.println("方法执行之前");  
    }  
}  

AopContext

AopContext 是 Spring AOP 的一个接口,它提供了一种机制来访问当前的 AOP 代理对象。通过 AopContext,您可以在需要时获取到当前执行上下文中的代理对象。这在某些情况下非常有用,例如当您希望在同一个类的不同方法之间进行方法调用,并且希望能够应用切面逻辑时。

例子

启用 AopContext: 在配置类中设置 exposeProxy 属性为 true,这样可以将代理对象暴露到 AopContext 中。

@EnableAspectJAutoProxy(exposeProxy = true)  
public class AppConfig {  
    // ...  
}  

获取代理对象: 在需要使用 AOP 功能的地方,调用 AopContext.currentProxy() 来获取当前的 AOP 代理对象。

import org.aspectj.lang.annotation.Aspect;  
import org.aspectj.lang.annotation.Before;  
import org.aspectj.lang.annotation.Pointcut;  
import org.springframework.aop.framework.AopContext;  
import org.springframework.context.annotation.Bean;  
import org.springframework.context.annotation.Configuration;  
import org.springframework.context.annotation.EnableAspectJAutoProxy;  

@Configuration  
@EnableAspectJAutoProxy(exposeProxy = true)  
public class AppConfig {  

    @Bean  
    public MyService myService() {  
        return new MyServiceImpl();  
    }  

    @Bean  
    public MyAspect myAspect() {  
        return new MyAspect();  
    }  
}  

@Aspect  
class MyAspect {  

    @Before("execution(* com.example.service.*.*(..))")  
    public void beforeAdvice() {  
        System.out.println("方法执行之前");  
    }  
}  

interface MyService {  
    void doSomething();  
}  

class MyServiceImpl implements MyService {  
    public void doSomething() {  
        System.out.println("执行服务逻辑");  
        // 可以通过 AopContext 获取代理对象  
        MyService proxy = (MyService) AopContext.currentProxy();  
        proxy.doSomething();  // 这次调用会触发切面  
    }  
}

当您希望在同一个类的不同方法之间进行方法调用,并且希望能够应用切面逻辑时

import org.aspectj.lang.annotation.Aspect;  
import org.aspectj.lang.annotation.Before;  
import org.springframework.aop.framework.AopContext;  
import org.springframework.context.annotation.Bean;  
import org.springframework.context.annotation.Configuration;  
import org.springframework.context.annotation.EnableAspectJAutoProxy;  

@Configuration  
@EnableAspectJAutoProxy(exposeProxy = true)  
public class AppConfig {  

    @Bean  
    public MyService myService() {  
        return new MyServiceImpl();  
    }  

    @Bean  
    public MyAspect myAspect() {  
        return new MyAspect();  
    }  
}  

@Aspect  
class MyAspect {  

    @Before("execution(* com.example.service.MyService.*(..))")  
    public void beforeAdvice() {  
        System.out.println("方法执行之前");  
    }  
}  

interface MyService {  
    void someMethod();  
}  

class MyServiceImpl implements MyService {  
    @Override  
    public void someMethod() {  
        System.out.println("执行服务逻辑");  
        // 调用另一个方法  
        this.anotherMethod(); // 这里不会触发切面  
        // 使用 AopContext 获取代理对象并调用  
        MyService proxy = (MyService) AopContext.currentProxy();  
        proxy.anotherMethod(); // 这里会触发切面  
    }  

    public void anotherMethod() {  
        System.out.println("执行另一个方法");  
    }  
}

代码分析

  • @Aspect 注解的切面MyAspect 中定义了一个前置通知 beforeAdvice,它会在 MyService 接口中的任何方法执行之前被调用。
  • 服务实现类MyServiceImpl 实现了 MyService 接口。在 someMethod 方法中,首先是直接调用 anotherMethod(),这不会触发切面逻辑,因为调用的是同一对象的方法。
  • 获取代理对象:调用 AopContext.currentProxy() 获取代理对象,然后通过代理对象调用 anotherMethod(),这样可以确保切面逻辑能够正确执行。
posted @ 2024-09-25 09:39  CH_song  阅读(8)  评论(0编辑  收藏  举报