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并不能实现事务和异步,道理就是这样的。