Spring aop 自我调用的问题

项目中使用Spring aop进行拦截的时候,发现有的能拦截有的不能拦截。查看日志发现,单独调用方法的时候能被拦截,在被同一个类中的方法调用的时候不起作用。这里先说一下AOP拦截不到自我调用方法的原因:假设我们有一个类是ServiceA,这个类中有一个A方法,A方法中又调用了B方法。当我们使用AOP进行拦截的时候,首先会创建一个ServiceA的代理类,其实在我们的系统中是存在两个ServiceA的对象的,一个是目标ServiceA对象,一个是生成的代理ServiceA对象,如果在代理类的A方法中调用代理类的B方法,这个时候AOP拦截是可以生效的,但是如果在代理类的A方法中调用目标类的B方法,这个时候AOP拦截是不生效的,**大多数情况下我们都是在代理类的A方法中直接调用目标类的B方法**。

看代码说话:两个方法设置不同的事务级别,selfCallB()设置为开启一个新的事物

public interface SelfCallService {
    /**
     * 方法A
     */
    void selfCallA();

    /**
     * 方法B
     */
    void selfCallB();
}
实现类
@Service
public class SelfCallServiceImpl implements SelfCallService{
    /**
     * 方法A
     */
    @Transactional()
    public void selfCallA() {
        System.out.println("我是方法A");
        System.out.println("是否是AOP拦截:" + AopUtils.isAopProxy(this));
        this.selfCallB();
    }

    /**
     * 方法B
     */
    @Transactional(propagation=Propagation.REQUIRES_NEW)
    public void selfCallB() {
        System.out.println("我是方法B");
    }
}

通过日志发现 selfCallB()没有新建一个事物,而是加入到selfCallA()的事物中。所以只要改为通过代理类调用selfCallB()方法就行。


解决方法:

  1. 通过ThreadLocal暴露代理对象
xml形式:
<aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"/>
注解形式:
@EnableAspectJAutoProxy(exposeProxy = true,proxyTargetClass = true)


@Service
public class SelfCallServiceImpl implements SelfCallService {
    /**
     * 方法A
     */
    public void selfCallA() {
        //通过暴露ThreadLocal的方式获取代理对象
        ((SelfCallService)AopContext.currentProxy()).selfCallB();
    }
    /**
     * 方法B
     */
    public void selfCallB() {
        System.out.println("我是方法B");
    }
}

2.通过初始化方法的方式:

@Service
public class SelfCallServiceImpl implements SelfCallService{
    //注入ApplicationContext对象
    @Autowired
    //(1)
    private ApplicationContext applicationContext;
    //(2)
    private SelfCallService selfCallService;
    //(3)
    //在所有属性被设置完值之后被调用(在Spring容器的声明周期中也只会被调用一次)
    //也可以通过实现InitializingBean接口,实现afterPropertiesSet方法 如果是使用XML配置的话,也可以通过指定init-method的方式
    //执行顺序PostConstruct-->afterPropertiesSet-->init-method
    @PostConstruct
    public void setSelfCallService(){
        selfCallService = applicationContext.getBean(SelfCallServiceImpl.class);
    }
    /**
     * 方法A
     */
    public void selfCallA() {
        //第二种方式 从上下文中获取被代理的对象 标号为(1)、(2)、(3)、(4)的就是第二种实现自我调用的方式
        //这种方式的缺点是:不能解决scope为prototype的bean。
        //(4)
        selfCallService.selfCallB();
    }
    /**
     * 方法B
     */
    public void selfCallB() {
        System.out.println("我是方法B");
    }
}

3、通过BeanPostProcessor的方式

https://blog.csdn.net/zknxx/article/details/72585822

猜你喜欢

转载自blog.csdn.net/jing956899449/article/details/81356989
今日推荐