事务失效、AOP切入等同类调用方法问题解决

一、

切面问题:
在开发中会遇到,当在Service中定义了一个方法并且切入之后,从Controller里面调用该方法可以实现切入,但是当在同一个Service中实现另一方法并调用改方法时却无法切入
例如同一个类中方法A,调用它的方法B,这时B方法是无法切入的。

事务问题:
同一个类中方法A没有事务,调用它的方法B,B有事务。这是事务失效。

其实原因很简单,Spring在扫描Bean的时候会自动为标注了@Transactional注解的类生成一个代理类(proxy),当有注解的方法被调用的时候,实际上是代理类调用的,代理类在调用之前会开启事务,执行事务的操作,但是同类中的方法互相调用,相当于this.B(),此时的B方法并非是代理类调用,而是直接通过原有的Bean直接调用,所以注解会失效。
切面也是同理。都是通过代理类执行的。 所有解决此问题就是通过获取代理类去调用方法B

二、这里有两种解决方案,建议使用方案二。

方案一:
通过AopContext.currentProxy()获取代理类,使用它时需要在启动类上设置@EnableAspectJAutoProxy(exposeProxy=true)注解,并exposeProxy设置为true

	public void saveUser(User user) {
        //获取代理类
        UserServiceImpl userService = (UserServiceImpl) AopContext.currentProxy();
        userService.test(user);
    }

    @Transactional
    public User test(User user) {
        User user1 = userRespository.save(user);
        int i = 1/0;
        return user1;
    }

方案二:
从spring容器中获取bean,通过bean调用。

	public void saveUser(User user) {
        //获取bean
        UserServiceImpl bean = applicationContext.getBean(UserServiceImpl.class);
        bean.test(user);
    }

    @Transactional
    public User test(User user) {
        User user1 = userRespository.save(user);
        int i = 1/0;
        return user1;
    }
<think>嗯,用户让我介绍一下Spring切面,首先我需要回忆一下Spring AOP的相关知识。Spring切面是AOP(面向切面编程)的核心概念之一,用于模块化横切关注点,比如日志、事务管理、安全等。用户可能想了解切面的定义、组成部分以及如何使用。 首先,我应该解释AOP的基本概念,然后切入切面的具体内容。切面通常包含通知(Advice)、切入点(Pointcut)、连接点(Join Point)等元素。需要详细说明每个部分的作用,比如通知定义了切面在何时执行,切入点指定了哪些地方应用通知,连接点则是程序执行中的具体点,如方法调用或异常抛出。 然后,应该给出一个代码示例,展示如何用Spring创建切面。例如,使用@Aspect注解定义一个切面类,用@Before、@After等注解来声明不同类型的通知。同时,需要用到切入点表达式,比如execution(* com.example.service.*.*(..)),来说明切入点如何匹配方法。 接下来,可能需要解释各个通知类型:前置通知、后置通知、环绕通知、返回后通知和异常通知。说明它们分别在方法执行的哪个阶段被触发。例如,@Around可以控制方法的执行,允许在前后添加逻辑,甚至决定是否执行方法。 然后,应该讨论切面的应用场景,比如日志记录、性能监控、事务管理等,这些横跨多个模块的功能。同时,可以提到切面的优势,比如减少代码重复,提高模块化程度,以及如何通过配置文件或注解来配置切面。 最后,在相关问题部分,需要设计几个与AOP相关的问题,涵盖基本概念、通知类型、切入点表达式、动态代理等。确保问题能够引导用户进一步深入学习。</think><< Spring切面(Aspect)是Spring AOP(面向切面编程)的核心组件,用于模块化横切关注点(如日志、事务、权限等)。切面通过定义**通知(Advice)**和**切入点(Pointcut)**,将代码逻辑织入目标方法的特定位置。 ### 1. **切面的核心组成** - **通知(Advice)**:定义切面代码的执行时机。 - `@Before`: 目标方法执行前 - `@AfterReturning`: 目标方法成功返回后 - `@AfterThrowing`: 目标方法抛出异常后 - `@After`: 目标方法执行后(无论是否成功) - `@Around`: 包裹目标方法(控制方法执行) - **切入点(Pointcut)**:通过表达式定义哪些方法需要被切面处理。 示例:`execution(* com.example.service.*.*(..))` 匹配`service`包下所有类的所有方法。 - **连接点(Join Point)**:程序执行过程中的具体触发点(如方法调用、异常抛出)。 ### 2. **代码示例** ```java @Aspect @Component public class LoggingAspect { // 定义切入点:匹配service包下的所有方法 @Pointcut("execution(* com.example.service.*.*(..))") public void serviceMethods() {} // 前置通知 @Before("serviceMethods()") public void logBefore(JoinPoint joinPoint) { System.out.println("调用方法: " + joinPoint.getSignature().getName()); } // 环绕通知(可控制方法是否执行) @Around("serviceMethods()") public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("方法开始执行"); Object result = joinPoint.proceed(); // 执行目标方法 System.out.println("方法执行结束"); return result; } } ``` ### 3. **配置启用AOP** 在Spring Boot中,添加`@EnableAspectJAutoProxy`注解: ```java @Configuration @EnableAspectJAutoProxy public class AppConfig {} ``` ### 4. **切面的实现原理** Spring AOP基于动态代理(JDK动态代理或CGLIB): - 对接口实现类使用**JDK动态代理**。 - 对无接口的类使用**CGLIB生成子类代理**。 ### 5. **典型应用场景** - 日志记录 - 事务管理(@Transactional底层依赖AOP) - 权限校验 - 性能监控 ### 6. **常见问题** - **切入点表达式错误**:如`execution`语法错误导致切面未生效。 - **代理失效**:同类内部方法调用(未通过代理对象)导致切面失效。 - **执行顺序冲突**:多个切面通过`@Order`注解指定优先级。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值