浏览 5075 次
锁定老帖子 主题:SpringAOP嵌套调用的解决办法
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2007-08-06
public class SomeServiceImpl implements SomeService { public void someMethod() { someInnerMethod(); //foo... } public void someInnerMethod() { //bar... } } 两个方法经过AOP代理,执行时都实现系统日志记录。单独使用someInnerMethod时,没有任何问题。但someMethod就有问题了。someMethod里调用的someInnerMethod方法是原始的,未经过AOP增强的。我们期望调用一次someMethod会记录下两条系统日志,分别是someInnerMethod和someMethod的,但实际上只能记录下someMethod的日志,也就是只有一条。在配置事务时也可能会出现问题,比如someMethod方法是REQUIRED,someInnerMethod方法是REQUIRES_NEW,someInnerMethod的配置将不起作用,与someMethod方法会使用同一个事务,不会按照所配置的打开新事务。 由于java这个静态类型语言限制,最后想到个曲线救国的办法,出现这种特殊情况时,不要直接调用自身方法,而通过AOP代理后的对象。在实现里保留一个AOP代理对象的引用,调用时通过这个代理即可。比如: //从beanFactory取得AOP代理后的对象 SomeService someServiceProxy = (SomeService)beanFactory.getBean("someService"); //把AOP代理后的对象设置进去 someServiceProxy.setSelf(someServiceProxy); //在someMethod里面调用self的someInnerMethod,这样就正确了 someServiceProxy.someMethod(); 但这个代理对象还要我们手动set进来,幸好SpringBeanFactory有BeanPostProcessor扩展,在bean初始化前后会统一传递给BeanPostProcess处理,繁琐的事情就可以交给程序了,代码如下,首先定义一个BeanSelfAware接口,实现了此接口的程序表明需要注入代理后的对象到自身。 public class SomeServiceImpl implements SomeService,BeanSelfAware { private SomeService self;//AOP增强后的代理对象 //实现BeanSelfAware接口 public void setSelf(Object proxyBean) { this.self = (SomeService)proxyBean } public void someMethod() { someInnerMethod();//注意这句,通过self这个对象,而不是直接调用的 //foo... } public void someInnerMethod() { //bar... } } 再定义一个BeanPostProcessor,beanFactory中的每个Bean初始化完毕后,调用所有BeanSelfAware的setSelf方法,把自身的代理对象注入自身…… public class InjectBeanSelfProcessor implements BeanPostProcessor { public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if(bean instanceof BeanSelfAware) { System.out.println("inject proxy:" + bean.getClass()); BeanSelfAware myBean = (BeanSelfAware)bean; myBean.setSelf(bean); return myBean; } return bean; } public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } } 最后,在BeanFactory配置中组合起来,只需要把BeanPostProcesser加进去就可以了,比平常多一行配置而已。 <!-- 注入代理后的bean到bean自身的BeanPostProcessor... --> <bean class=" org.mypackage.InjectBeanSelfProcessor"></bean> <bean id="someServiceTarget" class="org.mypackage.SomeServiceImpl" /> <bean id="someService" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target"> <ref local="someServiceTarget" /> </property> <property name="interceptorNames"> <list> <value>someAdvisor</value> </list> </property> </bean> <!-- 调用spring的DebugInterceptor记录日志,以确定方法是否被AOP增强 --> <bean id="debugInterceptor" class="org.springframework.aop.interceptor.DebugInterceptor" /> <bean id="someAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="advice"> <ref local="debugInterceptor" /> </property> <property name="patterns"> <list> <value>.*someMethod</value> <value>.*someInnerMethod</value> </list> </property> </bean> 这里的someService#someInnerMethod就表现出预期的行为了,无论怎样,它都是经过AOP代理的,执行时都会输出日志信息。 用XmlBeanFactory进行测试需要注意,所有的BeanPostProcessor并不会自动生效,需要执行以下代码: XmlBeanFactory factory = new XmlBeanFactory(...); InjectBeanSelfProcessor postProcessor = new InjectBeanSelfProcessor(); factory.addBeanPostProcessor(postProcessor); ft:发完帖再看论坛里之前的帖子,发现居然更新了,而且取名都叫做self…… http://www.iteye.com/post/347986 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2007-08-06
请问能否以这个帖子的例子来具体说明呢?
http://www.iteye.com/post/347986 |
|
返回顶楼 | |
发表时间:2007-08-06
楼主搞的这个解决办法比较有意思。
|
|
返回顶楼 | |
发表时间:2007-08-07
yeshucheng 写道 请问能否以这个帖子的例子来具体说明呢?
http://www.iteye.com/post/347986 相同的原理啊,只是那篇帖子的配置有几个trick的地方。首先是这段 <bean id="someService" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"><value>aop.SomeService</value></property> <property name="target"><ref local="someServiceTarget"/></property> <property name="interceptorNames"> <list> <value>someAdvisor</value> </list> </property> </bean> 声明proxyInterfaces为SomeService,那么在BeanPostProcessor里得到的代理后的Bean只具有SomeService接口,而没有所需的BeanSelfAware,所以不会把代理过的bean通过self注入进去。(事实上在初始化过程中,InjectBeanSelfProcessor会对someService注入两次self,第一次是BeanFactory生成someServiceTarget,第二次是产生someService的代理后)。其实不声明proxyInterfaces,SpringAOP照样跑 另外用了正则表达式配置Pointcut,这个正则表达式有问题,使用CGLIB时拦截器根本不会起作用,因为通过CGLIB代理后的类都是这样的:“aop.SomeServiceImpl$$EnhancerByCGLIB$$88d3ec43” <bean id="someAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="advice"><ref local="debugInterceptor"/></property> <property name="patterns"> <list> <value>aop\.SomeService\.someMethod</value> <value>aop\.SomeService\.someInnerMethod</value> </list> </property> </bean> 这里有一份源码,就是用那个帖子修改的。 |
|
返回顶楼 | |