SpringAOP嵌套调用的解决办法

Spring AOP在同一个类里自身方法相互调用时无法拦截。比如下面的代码:

Java代码   收藏代码
  1. public class SomeServiceImpl implements SomeService  
  2. {  
  3.   
  4.     public void someMethod()  
  5.     {  
  6.         someInnerMethod();  
  7.         //foo...  
  8.     }  
  9.   
  10.     public void someInnerMethod()  
  11.     {  
  12.         //bar...  
  13.     }  
  14. }  


两个方法经过AOP代理,执行时都实现系统日志记录。单独使用someInnerMethod时,没有任何问题。但someMethod就有问题 了。someMethod里调用的someInnerMethod方法是原始的,未经过AOP增强的。我们期望调用一次someMethod会记录下两条 系统日志,分别是someInnerMethod和someMethod的,但实际上只能记录下someMethod的日志,也就是只有一条。在配置事务 时也可能会出现问题,比如someMethod方法是REQUIRED,someInnerMethod方法是 REQUIRES_NEW,someInnerMethod的配置将不起作用,与someMethod方法会使用同一个事务,不会按照所配置的打开新事 务。
由于java这个静态类型语言限制,最后想到个曲线救国的办法,出现这种特殊情况时,不要直接调用自身方法,而通过AOP代理后的对象。在实现里保留一个AOP代理对象的引用,调用时通过这个代理即可。比如:

Java代码   收藏代码
  1. //从beanFactory取得AOP代理后的对象  
  2. SomeService someServiceProxy = (SomeService)beanFactory.getBean("someService");   
  3.   
  4. //把AOP代理后的对象设置进去  
  5. someServiceProxy.setSelf(someServiceProxy);   
  6.   
  7. //在someMethod里面调用self的someInnerMethod,这样就正确了  
  8. someServiceProxy.someMethod();  


但这个代理对象还要我们手动set进来,幸好SpringBeanFactory有BeanPostProcessor扩展,在bean初始化前 后会统一传递给BeanPostProcess处理,繁琐的事情就可以交给程序了,代码如下,首先定义一个BeanSelfAware接口,实现了此接口 的程序表明需要注入代理后的对象到自身。

Java代码   收藏代码
  1. public class SomeServiceImpl implements SomeService,BeanSelfAware  
  2.   
  3. {  
  4.   
  5.     private SomeService self;//AOP增强后的代理对象  
  6.   
  7.    
  8.   
  9.     //实现BeanSelfAware接口  
  10.   
  11.     public void setSelf(Object proxyBean)  
  12.   
  13.     {  
  14.   
  15.         this.self = (SomeService)proxyBean  
  16.   
  17.     }  
  18.   
  19.    
  20.   
  21.     public void someMethod()  
  22.   
  23.     {  
  24.   
  25.         someInnerMethod();//注意这句,通过self这个对象,而不是直接调用的  
  26.   
  27.         //foo...  
  28.   
  29.     }  
  30.   
  31.     public void someInnerMethod()  
  32.   
  33.     {  
  34.   
  35.         //bar...  
  36.   
  37.     }  
  38.   
  39. }  


再定义一个BeanPostProcessor,beanFactory中的每个Bean初始化完毕后,调用所有BeanSelfAware的setSelf方法,把自身的代理对象注入自身……

Java代码   收藏代码
  1. public class InjectBeanSelfProcessor implements BeanPostProcessor  
  2. {  
  3.    
  4.     public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException  
  5.     {  
  6.         if(bean instanceof BeanSelfAware)  
  7.         {  
  8.             System.out.println("inject proxy:" + bean.getClass());  
  9.             BeanSelfAware myBean = (BeanSelfAware)bean;  
  10.             myBean.setSelf(bean);  
  11.             return myBean;  
  12.         }  
  13.         return bean;  
  14.     }  
  15.    
  16.     public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException  
  17.     {  
  18.         return bean;  
  19.     }  
  20. }  


最后,在BeanFactory配置中组合起来,只需要把BeanPostProcesser加进去就可以了,比平常多一行配置而已。

Java代码   收藏代码
  1. <!-- 注入代理后的bean到bean自身的BeanPostProcessor... -->  
  2. <bean class=" org.mypackage.InjectBeanSelfProcessor"></bean>  
  3.   
  4. <bean id="someServiceTarget" class="org.mypackage.SomeServiceImpl" />   
  5.   
  6. <bean id="someService" class="org.springframework.aop.framework.ProxyFactoryBean">  
  7.     <property name="target">  
  8.         <ref local="someServiceTarget" />  
  9.     </property>  
  10.     <property name="interceptorNames">  
  11.         <list>  
  12.             <value>someAdvisor</value>  
  13.         </list>  
  14.     </property>  
  15. </bean>  
  16.   
  17. <!-- 调用spring的DebugInterceptor记录日志,以确定方法是否被AOP增强 -->  
  18. <bean id="debugInterceptor" class="org.springframework.aop.interceptor.DebugInterceptor" />  
  19.   
  20. <bean id="someAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">  
  21.     <property name="advice">  
  22.         <ref local="debugInterceptor" />  
  23.     </property>  
  24.     <property name="patterns">  
  25.         <list>  
  26.             <value>.*someMethod</value>  
  27.             <value>.*someInnerMethod</value>  
  28.         </list>  
  29.     </property>  
  30. </bean>  


这里的someService#someInnerMethod就表现出预期的行为了,无论怎样,它都是经过AOP代理的,执行时都会输出日志信息。
用XmlBeanFactory进行测试需要注意,所有的BeanPostProcessor并不会自动生效,需要执行以下代码:

Java代码   收藏代码
  1. XmlBeanFactory factory = new XmlBeanFactory(...);  
  2. InjectBeanSelfProcessor postProcessor = new InjectBeanSelfProcessor();  
  3. factory.addBeanPostProcessor(postProcessor);  


ft:发完帖再看论坛里之前的帖子,发现居然更新了,而且取名都叫做self……
http://www.iteye.com/post/347986

猜你喜欢

转载自zzc1684.iteye.com/blog/2173630