SpringAOP嵌套调用的解决办法

Spring AOP在同一个类里自身方法相互调用时无法拦截。比如下面的代码:
[code]
public class SomeServiceImpl implements SomeService
{

public void someMethod()
{
someInnerMethod();
//foo...
}

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

//把AOP代理后的对象设置进去
someServiceProxy.setSelf(someServiceProxy);

//在someMethod里面调用self的someInnerMethod,这样就正确了
someServiceProxy.someMethod();
[/code]
但这个代理对象还要我们手动set进来,幸好SpringBeanFactory有BeanPostProcessor扩展,在bean初始化前后会统一传递给BeanPostProcess处理,繁琐的事情就可以交给程序了,代码如下,首先定义一个BeanSelfAware接口,实现了此接口的程序表明需要注入代理后的对象到自身。
[code]
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...

}

}
[/code]
再定义一个BeanPostProcessor,beanFactory中的每个Bean初始化完毕后,调用所有BeanSelfAware的setSelf方法,把自身的代理对象注入自身……
[code]
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;
}
}
[/code]
最后,在BeanFactory配置中组合起来,只需要把BeanPostProcesser加进去就可以了,比平常多一行配置而已。
[code]
<!-- 注入代理后的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>
[/code]
这里的someService#someInnerMethod就表现出预期的行为了,无论怎样,它都是经过AOP代理的,执行时都会输出日志信息。
用XmlBeanFactory进行测试需要注意,所有的BeanPostProcessor并不会自动生效,需要执行以下代码:
[code]
XmlBeanFactory factory = new XmlBeanFactory(...);
InjectBeanSelfProcessor postProcessor = new InjectBeanSelfProcessor();
factory.addBeanPostProcessor(postProcessor);
[/code]
ft:发完帖再看论坛里之前的帖子,发现居然更新了,而且取名都叫做self……
http://www.iteye.com/post/347986
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值