首页 > 其他分享 >Spring-AOP

Spring-AOP

时间:2024-09-25 09:35:29浏览次数:7  
标签:匹配 int Spring 切面 AOP 方法 public

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(),这样可以确保切面逻辑能够正确执行。

标签:匹配,int,Spring,切面,AOP,方法,public
From: https://www.cnblogs.com/21CHS/p/18430634

相关文章

  • Spring-IOC
    Spring-容器篇IoC、DI注册组件注入组件组件生命周期组件和容器组件:具有一定功能的对象容器:管理组件(创建、获取、保存、销毁)IoC和DIIoC:InversionofControl(控制反转)控制:资源的控制权(资源的创建、获取、销毁等)反转:和传统的方式不一样了DI:DependencyInjection(......
  • Spring-MVC
    Spring-MVC介绍https://docs.spring.io/spring-framework/reference/web/webmvc.htmlSpringWebMVC是基于ServletAPI构建的原始Web框架,从一开始就包含在SpringFramework中。正式名称“SpringWebMVC”来自其源模块的名称(spring-webmvc),但它通常被称为“SpringMVC”。在......
  • 【开题报告】基于Springboot+vue基于网上商品销售管理系统(程序+源码+论文) 计算机毕业
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着互联网技术的飞速发展,电子商务已成为现代商业活动的重要组成部分,深刻改变了人们的购物习惯和消费模式。网上商品销售管理系统作为电子商务平台的......
  • Spring面试题
    1.Spring框架基本理解关键词:核心思想IOC/AOP、作用(解耦、简化),简单描述框架组成Spring是一个企业级、轻量级开源的分层架构核心思想:IOC(控制反转)和AOP(面向切面编程)1.为Java应用程序开发提供组件管理服务;2.用于组件之间的解耦 3.简化第三方JavaEE中间技术的使用Spring框架包......
  • [含文档+PPT+源码等]精品基于springboot实现的原生Andriod学生宿舍管理系统
    基于SpringBoot实现的原生Android学生宿舍服务系统的背景,可以从以下几个方面进行阐述:一、技术背景SpringBoot的优势:SpringBoot通过其自动配置、简化依赖管理、内嵌容器等特性,极大地简化了基于Spring框架的应用开发过程。这使得开发者能够快速搭建起稳定、可靠的后端服......
  • 基于SpringBoot的书城系统
    系列文章目录1.基于SSM的洗衣房管理系统+原生微信小程序+LW参考示例2.基于SpringBoot的宠物摄影网站管理系统+LW参考示例3.基于SpringBoot+Vue的企业人事管理系统+LW参考示例4.基于SSM的高校实验室管理系统+LW参考示例5.基于SpringBoot的二手数码回收系统+原生微信小......
  • SpringBoot集成JPA及基本使用
    参考地址: https://blog.csdn.net/JingAi_jia917/article/details/138159418前言在讲解SpringBoot集成JPA之前,先简单了解一下几个概念,JDBC、ORM、JPA以及SpringDataJPA。1.1JDBCJDBC(JavaDataBaseConnectivity),是java连接数据库操作的原生接口API,为开发者访问数据库提供标准......
  • 【万字文档+PPT+源码】基于springboot+vue医院挂号系统-可用于毕设-课程设计-练手学习
    博主简介:......
  • 【万字文档+PPT+源码】基于springboot+vue新闻发布系统-可用于毕设-课程设计-练手学习
    博主简介:......
  • aop动态代理可以用两种技术jdk动态代理和cglib动态代理
    SpringAOP(面向切面编程)在实现动态代理时,实际上可以使用两种不同的技术:JDK动态代理和CGLIB动态代理。具体使用哪一种技术取决于具体的情况。1.**JDK动态代理**:-JDK动态代理只能代理实现了接口的类。-如果目标类实现了一个或多个接口,SpringAOP默认会使用JDK动态代理。......