1.问题描述
目标对象内部的函数自我调用时被调函数将忽略切面。
1)实例
SelfInvokable.java
public interface SelfInvokable { void invoke(); void method(); }
SelfInvoker.java
public class SelfInvoker implements SelfInvokable { public void invoke() { System.out.println("in SelfInvoker.invoke..."); } public void method() { System.out.println("in SelfInvoker.method..."); this.invoke(); } }
SelfInvoker2.java
public class SelfInvoker2 { public void invoke() { System.out.println("in SelfInvoker2.invoke..."); } public void method() { System.out.println("in SelfInvoker2.method..."); this.invoke(); } }
spring-aop.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <bean id="selfInvoker" class="com.siyuan.study.spring.aop.SelfInvoker"/> <bean id="selfInvoker2" class="com.siyuan.study.spring.aop.SelfInvoker2"/> <bean id="beforeAdvisor" class="com.siyuan.study.spring.aop.advisor.BeforeAdvisor"/> <aop:config> <aop:pointcut id="selfInvokerPointcut" expression="execution(* com.siyuan.study.spring.aop.*.*(..))" /> <aop:advisor advice-ref="beforeAdvisor" pointcut-ref="selfInvokerPointcut"/> </aop:config> </beans>
SelfInvokerAOPTest.java
public class SelfInvokerAOPTest { private ApplicationContext ctxt; @Before public void setup() { ctxt = new ClassPathXmlApplicationContext("spring-aop.xml"); } @Test public void testSelfInvokable() { SelfInvokable selfInvoker = (SelfInvokable) ctxt.getBean("selfInvoker"); System.out.println("JDK proxy : " + AopUtils.isJdkDynamicProxy(selfInvoker)); selfInvoker.invoke(); selfInvoker.method(); } @Test public void testSelfInvokable2() { SelfInvoker2 selfInvoker2 = (SelfInvoker2) ctxt.getBean("selfInvoker2"); System.out.println("CGLIB proxy : " + AopUtils.isCglibProxy(selfInvoker2)); selfInvoker2.invoke(); selfInvoker2.method(); } }
执行结果:
JDK proxy : true before invoke in SelfInvoker.invoke... before method in SelfInvoker.method... in SelfInvoker.invoke... CGLIB proxy : true before invoke in SelfInvoker2.invoke... before method in SelfInvoker2.method... in SelfInvoker2.invoke...
2)分析:
Spring AOP实现代理有两种方式
--JDK:目标对象有实现接口时,仅对目标对象实现接口中的方法进行拦截,接口中未包含的方法将不进行拦截。
--CGLIB:目标对象未实现任何接口时。
从执行结果来可看出,两种不同方式获得的代理在函数自我调用时被调用函数均忽略了切面。
2.修复
1)AopContext获取代理对象,实现原理ThreadLocal
SelfInvokable.java
public interface SelfInvokable { ... void methodFix(); }
SelfInvoker.java
public class SelfInvoker implements SelfInvokable { ... public void methodFix() { System.out.println("in SelfInvoker.methodFix..."); ((SelfInvokable) AopContext.currentProxy()).invoke(); } }
SelfInvoker2.java
public class SelfInvoker2 { ... public void methodFix() { System.out.println("in SelfInvoker2.methodFix..."); ((SelfInvoker2) AopContext.currentProxy()).invoke(); } }
spring-aop.xml
... <aop:config expose-proxy="true"> ...
SelfInvokerAOPTest.java
@Test public void testSelfInvokable() { ... selfInvoker.methodFix(); } @Test public void testSelfInvokable2() { ... selfInvoker2.methodFix(); }
执行结果
JDK proxy : true before invoke in SelfInvoker.invoke... before method in SelfInvoker.method... in SelfInvoker.invoke... before methodFix in SelfInvoker.methodFix... before invoke in SelfInvoker.invoke... CGLIB proxy : true before invoke in SelfInvoker2.invoke... before method in SelfInvoker2.method... in SelfInvoker2.invoke... before methodFix in SelfInvoker2.methodFix... before invoke in SelfInvoker2.invoke...
2)BeanPostProcessor+BeanSelfAware(标记需要自调用的接口)
BeanSelfAware.java
public interface BeanSelfAware { void setSelf(Object self); }
SelfInvoker.java
... public void methodFix() { System.out.println("in SelfInvoker.methodFix..."); //((SelfInvokable) AopContext.currentProxy()).invoke(); self.invoke(); } private SelfInvokable self; public void setSelf(Object self) { this.self = (SelfInvokable) self; } ...
InjectBeanSelfProcessor.java
public class InjectBeanSelfProcessor implements BeanPostProcessor { public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if(bean instanceof BeanSelfAware) { BeanSelfAware myBean = (BeanSelfAware)bean; myBean.setSelf(bean); return myBean; } return bean; } public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } }
spring-aop.xml
... <bean class="com.siyuan.study.spring.extension.InjectBeanSelfProcessor"/> ...
SelfInvokerAOPTest.java
... @Test public void testSelfInvokable() { SelfInvokable selfInvoker = (SelfInvokable) ctxt.getBean("selfInvoker"); //System.out.println("JDK proxy : " + AopUtils.isJdkDynamicProxy(selfInvoker)); //selfInvoker.invoke(); //selfInvoker.method(); selfInvoker.methodFix(); } ...
运行结果
before setSelf before methodFix in SelfInvoker.methodFix... before invoke in SelfInvoker.invoke... before setSelf
3.参考资料
http://blog.163.com/yf_198407/blog/static/513854112012621105114276/