`
zzc1684
  • 浏览: 1191820 次
  • 性别: Icon_minigender_1
  • 来自: 广州
文章分类
社区版块
存档分类
最新评论

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

分享到:
评论

相关推荐

    通用的报表缓存设计(Spring AOP + Redis)

    通用的报表缓存设计项目背景现状态方案代码实现Redis配置注解AOP缓存(关键)业务调用(部分)测试结果:第一次第二次测试结果总结 项目背景 1:用户群体大, 2:业务计算量大,计算逻辑复杂。 现状态 1:主面页面,...

    高级开发spring面试题和答案.pdf

    为什么同一个类A调用b方法事务,A方法一定要有事务(编码式的不用) @transaction多个数据源事务怎么指定数据源 传播特性有几种?7种; 某一个事务嵌套另一个事务的时候怎么办? REQUIRED_NEW和REQUIRED区别 Spring...

    Spring 2.0 开发参考手册

    6.4.2. Spring AOP中使用@AspectJ还是XML? 6.5. 混合切面类型 6.6. 代理机制 6.7. 编程方式创建@AspectJ代理 6.8. 在Spring应用中使用AspectJ 6.8.1. 在Spring中使用AspectJ来为domain object进行依赖注入 ...

    Spring API

    9.9. 常见问题的解决方法 9.9.1. 对一个特定的 DataSource 使用了错误的事务管理器 9.10. 更多的资源 10. DAO支持 10.1. 简介 10.2. 一致的异常层次 10.3. 一致的DAO支持抽象类 11. 使用JDBC进行数据访问 ...

    Spring中文帮助文档

    9.9. 常见问题的解决方法 9.9.1. 对一个特定的 DataSource 使用了错误的事务管理器 9.10. 更多的资源 10. DAO支持 10.1. 简介 10.2. 一致的异常层次 10.3. 一致的DAO支持抽象类 11. 使用JDBC进行数据访问 ...

    spring chm文档

    6.4.2. Spring AOP中使用@AspectJ还是XML? 6.5. 混合切面类型 6.6. 代理机制 6.7. 编程方式创建@AspectJ代理 6.8. 在Spring应用中使用AspectJ 6.8.1. 在Spring中使用AspectJ来为domain object进行依赖注入 ...

    Spring.3.x企业应用开发实战(完整版).part2

    10.3 事务方法嵌套调用的迷茫 10.3.1 Spring事务传播机制回顾 10.3.2 相互嵌套的服务方法 10.4 多线程的困惑 10.4.1 Spring通过单实例化Bean简化多线程问题 10.4.2 启动独立线程调用事务方法 10.5 联合军种作战的...

    Spring-Reference_zh_CN(Spring中文参考手册)

    6.4.2. Spring AOP中使用@AspectJ还是XML? 6.5. 混合切面类型 6.6. 代理机制 6.7. 编程方式创建@AspectJ代理 6.8. 在Spring应用中使用AspectJ 6.8.1. 在Spring中使用AspectJ来为domain object进行依赖注入 6.8.1.1....

    Spring3.x企业应用开发实战(完整版) part1

    10.3 事务方法嵌套调用的迷茫 10.3.1 Spring事务传播机制回顾 10.3.2 相互嵌套的服务方法 10.4 多线程的困惑 10.4.1 Spring通过单实例化Bean简化多线程问题 10.4.2 启动独立线程调用事务方法 10.5 联合军种作战的...

    千方百计笔试题大全

    34、是否可以从一个static 方法内部发出对非static 方法的调用? 11 35、写clone()方法时,通常都有一行代码,是什么? 11 36、GC 是什么? 为什么要有GC? 11 37、垃圾回收的优点和原理。并考虑2 种回收机制。 11 38...

    java面试宝典

    34、是否可以从一个static 方法内部发出对非static 方法的调用? 11 35、写clone()方法时,通常都有一行代码,是什么? 11 36、GC 是什么? 为什么要有GC? 11 37、垃圾回收的优点和原理。并考虑2 种回收机制。 11 38...

    asp.net知识库

    2.0问题、错误解决办法 ASP.NET 2.0使用Web Part创建应用程序之二(共二) 体验 .net2.0 的优雅(2) -- ASP.net 主题和皮肤 NET2.0系列介绍(一).NET 2.0 中Web 应用程序主题的切换 ASP.NET 2.0 中Web 应用程序...

    最新Java面试宝典pdf版

    13、是否可以从一个static方法内部发出对非static方法的调用? 12 14、Integer与int的区别 13 15、Math.round(11.5)等於多少? Math.round(-11.5)等於多少? 13 16、下面的代码有什么不妥之处? 13 17、请说出作用域...

    Java面试宝典2010版

    13、是否可以从一个static方法内部发出对非static方法的调用? 14、Integer与int的区别 15、Math.round(11.5)等於多少? Math.round(-11.5)等於多少? 16、下面的代码有什么不妥之处? 17、请说出作用域public,...

    Java面试笔试资料大全

    13、是否可以从一个static方法内部发出对非static方法的调用? 12 14、Integer与int的区别 13 15、Math.round(11.5)等於多少? Math.round(-11.5)等於多少? 13 16、下面的代码有什么不妥之处? 13 17、请说出作用域...

    JAVA面试宝典2010

    13、是否可以从一个static方法内部发出对非static方法的调用? 12 14、Integer与int的区别 13 15、Math.round(11.5)等於多少? Math.round(-11.5)等於多少? 13 16、下面的代码有什么不妥之处? 13 17、请说出作用域...

    Java面试宝典-经典

    13、是否可以从一个static方法内部发出对非static方法的调用? 12 14、Integer与int的区别 13 15、Math.round(11.5)等於多少? Math.round(-11.5)等於多少? 13 16、下面的代码有什么不妥之处? 13 17、请说出作用域...

    java面试题大全(2012版)

    13、是否可以从一个static方法内部发出对非static方法的调用? 12 14、Integer与int的区别 13 15、Math.round(11.5)等於多少? Math.round(-11.5)等於多少? 13 16、下面的代码有什么不妥之处? 13 17、请说出作用域...

Global site tag (gtag.js) - Google Analytics