Spring动态代理与AOP

AOP中的@Before是如何实现的?

在AOP(面向切面编程)中,@Before注解通常用于定义一个在方法执行前执行的前置通知(advice)。在Spring框架中,实现@Before功能的底层机制主要基于代理(Proxy)技术,这里的代理可以是JDK动态代理或者CGLIB代理,具体取决于你的配置或API使用情况。

以下是在Spring中使用@Before注解的@Before通知是如何实现的:

1. 配置方式

首先,你需要在Spring配置文件中或者使用@EnableAspectJAutoProxy注解来启用AOP支持。

例如,在XML配置中:

<aop:aspectj-autoproxy />

或使用Java配置:

@Configuration
@EnableAspectJAutoProxy
public class AopConfig {
}

2. Aspect定义

在Spring中,使用@Aspect注解来定义一个切面(Aspect),在切面中你可以定义多个通知(advice),包括@Before通知。

@Aspect
public class LoggingAspect {
    @Before("execution(* com.example.exampleClass.*(..))")
    public void beforeAdvice(JoinPoint joinPoint) {
        System.out.println("Method " + joinPoint.getSignature().getName() + " is called");
    }
}

3. 代理实现

当Spring识别到@Before通知后,它会创建一个代理对象,这个代理对象在调用目标方法之前会执行beforeAdvice方法。代理对象实际上是一个动态生成的类,它会通过JDK动态代理或CGLIB来创建。

  • JDK动态代理:如果目标方法的类使用了接口,并且Spring配置文件中启用了JDK代理,那么使用JDK动态代理创建代理对象。
  • CGLIB代理:如果目标方法的类不是接口或者Spring配置文件中启用了CGLIB代理,那么使用CGLIB创建代理对象。CGLIB库允许你动态地为一个类创建子类。

4. 调用执行

当你调用目标方法时,实际上是在调用代理对象的方法(前提是对象已被Spring容器进行管理。类似地,@Transactional 注解的作用是将一个方法声明为一个事务边界,其目的是确保方法周围的业务逻辑在进行数据库操作时能正确地进行事务管理。在Spring框架中,通常情况下,@Transactional注解需要在@Service注解的类或者方法上使用,这样Spring BootSpring MVC框架才能正确地识别出这是一个服务层的组件,并在需要的地方创建和管理代理对象,从而确保该方法所在的事务被正确地管理)。代理对象在调用方法之前会执行@Before通知中的方法,然后继续调用目标方法的实际逻辑。

总结

通过上述步骤,Spring能够通过动态代理技术,在目标方法执行之前自动调用@Before通知中的方法,实现程序的模块化、可维护性和可扩展性。这样的机制使得开发者能够专注于核心业务逻辑,而不需要在每一处方法调用中编写重复的前置检查代码。

@Transactional

当你在方法上使用 @Transactional 注解时,Spring AOP(面向切面编程)会自动地为这些方法生成一个代理对象。这个代理对象会在调用方法之前执行事务管理的逻辑,确保方法执行前后能正确地开始和提交或回滚事务。

实现原理:

  1. 配置:你需要在Spring配置文件中启用AOP支持,并且使用@EnableAutoConfiguration或者@ComponentScan注解扫描并管理你的组件。

    <context:component-scan base-package="com.example"/>
    <aop:aspectj-autoproxy/>
    

    或者

    @SpringBootApplication
    @ComponentScan
    @EnableAutoConfiguration
    public class MyApplication {
        public static void main(String[] args) {
            SpringApplication.run(MyApplication.class, args);
        }
    }
    
  2. 使用事务切面:Spring提供了一个事务切面类 TransactionAspectSupport 或者自定义一个继承 TransactionAspectSupport 的类,并在类中配置事务管理器(TransactionManager)。

    @Aspect
    public class TransactionAspect extends TransactionAspectSupport {
        @Autowired
        private DataSource dataSource;
    
        @Autowired
        private PlatformTransactionManager transactionManager;
    }
    
  3. 定义事务:在需要支持事务的方法上使用 @Transactional 注解。

    @Service
    public class MyService {
        @Transactional
        public void performTransaction() {
            // 业务逻辑
        }
    }
    
  4. 代理生成:Spring会自动识别这些 @Transactional 方法,并生成代理类。这个代理类会实现 InvocationHandler 接口,并在调用实际方法之前检查事务状态。

    当调用 performTransaction() 方法时,如果当前没有事务,则会开始一个新的事务;如果已经有事务,则在当前事务上下文中执行方法。方法执行完毕后,事务会根据调用 commit()rollback() 方法来确定是否提交或回滚事务。

总结:

实际上,当你在方法上使用 @Transactional 注解时,Spring会确保该方法被代理,使其能够正确地管理事务。这个代理过程是基于Spring的AOP机制,并且是在运行时动态生成的,无需开发者在类定义或方法声明中添加额外的代码。

类内部方法调用,会导致AOP失效吗?

在使用AOP(面向切面编程)时,直接在类方法内部调用其他方法并不会导致AOP失效。AOP的代理机制主要应用于类的实例化方法(如构造函数、初始化方法等)和实例方法,以及类的静态方法,只要这些方法被定义在代理对象上,那么无论它们内部调用其他方法,仍然能捕获到对应的切面逻辑。

然而,有几点需要特别注意:

  1. 方法调用层级:AOP代理只会在方法被外部直接调用时生效。如果在方法内部调用另一个方法,且这个调用是作为原始方法的一部分执行的,那么AOP仍然能作用于外部调用方法时。但是,嵌套调用的内部方法不会自动被AOP代理捕获,除非这些内部方法也被显式地用@Pointcut或者@Before@Around等注解定义为切点。

  2. 类的属性和静态方法:AOP对类的属性和静态方法的访问通常不会直接应用切面逻辑,除非使用特殊的切点(如@Around)来进行动态环绕处理或使用@Pointcut定义切点时考虑了静态方法的情况。

  3. 性能和资源消耗:在类内部方法调用中频繁添加切点可能会增加性能开销和内存消耗,因为每个调用都会创建额外的对象,并执行额外的逻辑。在设计时应权衡其对系统性能的影响。

  4. 测试和调试:内部调用在测试中可能会增加复杂性,因为它们可能绕过预期的切面逻辑。确保测试覆盖和调试时能够清楚地看到所有预期的AOP行为。

总之,类内部方法调用一般不会导致AOP失效,但需要正确地使用切点(如@Before@Around@Pointcut)来确保所有需要覆盖的逻辑都能够被AOP机制捕获。对于性能和测试考虑,设计时应有意识地考虑这些因素。

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
内部调用导致AOP失效:

在这里插入图片描述
@Transactional事务失效也是类似的场景。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你这个代码我看不懂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值