Spring事务处理时自我调用的解决方案 嵌套AOP

开涛的解决方案1 http://jinnianshilongnian.iteye.com/blog/1487235

 

AopContext.currentProxy() 原理

http://books.google.com.hk/books?id=jRVp2INtY1AC&pg=PA104&lpg=PA104&dq=Spring+AopContext&source=bl&ots=KXzss6euqJ&sig=B1LdkDTEACMgdmSSQp9SkMBPiwQ&hl=zh-CN&sa=X&ei=kx1vUoXIE4emkQWS_4CoBQ&ved=0CCkQ6AEwADgU#v=onepage&q&f=false 

 

解决方案2

 

通过BeanPostProcessor 在目标对象中注入代理对象

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>

 

 

 

posted @ 2013-10-29 10:38  大新博客  阅读(5842)  评论(0编辑  收藏  举报