Spring AOP无法调用自身方法的原因

版权声明:本文为博主原创文章,转载请指明文章出处! https://blog.csdn.net/u012385190/article/details/82119928

1、问题概述

在使用Spring AOP时,或多或少会碰到一些方法无法被增强的问题,有时同一个类里面的方法有的可以被增强,有的却无法被增强。要分析原因,首先要从Spring AOP的实现机制入手。

Aop底层实现有两种方法: 
1、基于JDK动态代理,通过接口来实现方法拦截,所以必须要确保要拦截的目标方法在接口中有定义,否则将无法实现拦截 
2、GCLib动态代理,通过动态生成子类来实现方法拦截,必须确保要拦截的目标方法可被子类访问,即目标方法必须定义为非final。且非私有实例方法。

2、例子

服务员业务类,代理目标类

public class Waiter {
    public void greetTo(String name) {
        System.out.println("Waiter Greet to " + name);
    }

    public void serverTo(String name) {
        System.out.println("Waiter Server to " + name);
    }
}

前置增强类

public class GreetBeforeAdivce implements MethodBeforeAdvice {
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        // 输出切点
        String clientName = (String) objects[0];
        System.out.println("How are you " + clientName + " ?(切点方法为:" + method + ")");
    }
}

beans-aware.xml配置

    <bean id="waiter" class="demo04.advisor.Waiter" />
    <bean id="greetBeforeAdviceAdvice" class="demo04.advisor.GreetBeforeAdivce" />
    <!--通过Advisor自动创建代理-->
    <bean id="regexpAdvisor"
          class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"
          p:patterns=".*To.*" p:advice-ref="greetBeforeAdviceAdvice"  />
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"  p:proxyTargetClass="true" />

测试类

public class AopAwareTest {
    @Test
    public void autoProxy(){
        String config="com/smart/autoproxy/beans-aware.xml";
        ApplicationContext ctx=new ClassPathXmlApplicationContext(config);
        Waiter waiter=(Waiter)ctx.getBean("waiter");
        waiter.greetTo("John");
        waiter.serveTo("John");
    }
}

输出(可以看到两个方法都被织入了增强,增强被正确地织入匹配的连接点中)

How are you John ?(切点方法为:public void demo04.advisor.Waiter.serverTo(java.lang.String))
Waiter Server to John
How are you John ?(切点方法为:public void demo04.advisor.Waiter.greetTo(java.lang.String))
Waiter Greet to John

现在将Waiter中的serverTo()方法改造一下,让serverTo调用greetTo方法,如下:

public class Waiter {
    public void greetTo(String name) {
        System.out.println("Waiter Greet to " + name);
        serverTo(name);//这里调用自身的severTo方法
    }

    public void serverTo(String name) {
        System.out.println("Waiter Server to " + name);
    }
}

再次运行测试类,输出为:

How are you John ?(切点方法为:public void demo04.advisor.Waiter.greetTo(java.lang.String))
Waiter Greet to John
Waiter Server to John
How are you John ?(切点方法为:public void demo04.advisor.Waiter.serverTo(java.lang.String))
Waiter Server to John

可以看出serveTo()和greetTo()都被织入了增强,但greetTo()内部调用的serverTo()没有被织入增强。

3、原因分析

上面例子中,在greetTo()方法里面直接调用serverTo(……)方法,这里还隐含一个关键字,那就是this,实际上这里调用是这样的:this.serverTo(),this是当前对象。而调用greetTo()是的对象是被代理的,在代理对象中执行增强后,通过invoke,用实际Waiter对象来调用greetTo()方法执行业务逻辑。在业务逻辑内又调用了serverTo(……)方法,调用的对象是当前对象,当前对象是Waiter,问题就出在这里,因为要想用执行serverTo方法的增强,必须用代理对象执行,但是此时却直接用Waiter对象调用,绕过了代理对象增强的部分,也就是说代理增强部分失效。在同一个类中使用@Transaction,@Async并不能实现事务和异步,道理就是这样的。

猜你喜欢

转载自blog.csdn.net/u012385190/article/details/82119928