SpringAOP嵌套调用的解决办法

 

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. }  

public class SomeServiceImpl implements SomeService

{

 

    public void someMethod()

    {

        someInnerMethod();

        //foo...

    }

 

    public void someInnerMethod()

    {

        //bar...

    }

}


两个方法经过AOP代理,执行时都实现系统日志记录。单独使用someInnerMethod时,没有任何问题。但someMethod就有问题了。someMethod里调用的someInnerMethod方法是原始的,未经过AOP增强的。我们期望调用一次someMethod会记录下两条系统日志,分别是someInnerMethodsomeMethod的,但实际上只能记录下someMethod的日志,也就是只有一条。在配置事务时也可能会出现问题,比如someMethod方法是REQUIREDsomeInnerMethod方法是REQUIRES_NEWsomeInnerMethod的配置将不起作用,与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里面调用selfsomeInnerMethod,这样就正确了   
  8. someServiceProxy.someMethod();  

//beanFactory取得AOP代理后的对象

SomeService someServiceProxy = (SomeService)beanFactory.getBean("someService");

 

//AOP代理后的对象设置进去

someServiceProxy.setSelf(someServiceProxy);

 

//someMethod里面调用selfsomeInnerMethod,这样就正确了

someServiceProxy.someMethod();


但这个代理对象还要我们手动set进来,幸好SpringBeanFactoryBeanPostProcessor扩展,在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这个对象,而不<SPAN class=hilite2></SPAN>直接调用的   
  26.   
  27.         //foo...   
  28.   
  29.     }   
  30.   
  31.     public void someInnerMethod()   
  32.   
  33.     {   
  34.   
  35.         //bar...   
  36.   
  37.     }   
  38.   
  39. }  

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...

 

    }

 

}


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

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. }  

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加进去就可以了,比平常多一行配置而已。

Java代码

  1. <!-- 注入代理后的beanbean自身的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<SPAN class=hilite1>DebugInterceptor</SPAN>记录日志,以确定方法<SPAN class=hilite2></SPAN>否被AOP增强 -->   
  18. <bean id="<SPAN class=hilite1>debugInterceptor</SPAN>" class="org.springframework.aop.interceptor.<SPAN class=hilite1>DebugInterceptor</SPAN>" />   
  19.   
  20. <bean id="someAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">   
  21.     <property name="advice">   
  22.         <ref local="<SPAN class=hilite1>debugInterceptor</SPAN>" />   
  23.     </property>   
  24.     <property name="patterns">   
  25.         <list>   
  26.             <value>.*someMethod</value>   
  27.             <value>.*someInnerMethod</value>   
  28.         </list>   
  29.     </property>   
  30. </bean>  

    <!-- 注入代理后的beanbean自身的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>

 

    <!-- 调用springDebugInterceptor记录日志,以确定方法是否被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并不会自动生效,需要执行以下代码:

Java代码

  1. XmlBeanFactory factory = new XmlBeanFactory(...);   
  2. InjectBeanSelfProcessor postProcessor = new InjectBeanSelfProcessor();   
  3. factory.addBeanPostProcessor(postProcessor);  

XmlBeanFactory factory = new XmlBeanFactory(...);

InjectBeanSelfProcessor postProcessor = new InjectBeanSelfProcessor();

factory.addBeanPostProcessor(postProcessor);


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

评论

fyting 2007-08-07

yeshucheng 写道

请问能否以这个帖子的例子来具体说明呢?
http://www.javaeye.com/post/347986


相同的原理啊,只是那篇帖子的配置有几个trick的地方。首先是这段

Java代码

  1. <bean id="someService" class="org.springframework.aop.framework.ProxyFactoryBean">     
  2.         <property name="proxyInterfaces"><value>aop.SomeService</value></property>     
  3.         <property name="target"><ref local="someServiceTarget"/></property>     
  4.         <property name="interceptorNames">     
  5.             <list>     
  6.                 <value>someAdvisor</value>     
  7.             </list>     
  8.         </property>     
  9.   </bean>  

<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>


声明proxyInterfacesSomeService,那么在BeanPostProcessor里得到的代理后的Bean只具有SomeService接口,而没有所需的BeanSelfAware,所以不会把代理过的bean通过self注入进去。(事实上在初始化过程中,InjectBeanSelfProcessor会对someService注入两次self,第一次是BeanFactory生成someServiceTarget,第二次是产生someService的代理后)。其实不声明proxyInterfacesSpringAOP照样跑
另外用了正则表达式配置Pointcut,这个正则表达式有问题,使用CGLIB时拦截器根本不会起作用,因为通过CGLIB代理后的类都是这样的:“aop.SomeServiceImpl$$EnhancerByCGLIB$$88d3ec43”

Java代码

  1. <bean id="someAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">   
  2.       <property name="advice"><ref local="<SPAN class=hilite1>debugInterceptor</SPAN>"/></property>   
  3.       <property name="patterns">   
  4.           <list>   
  5.               <value>aop/.SomeService/.someMethod</value>     
  6.               <value>aop/.SomeService/.someInnerMethod</value>     
  7.           </list>   
  8.       </property>   
  9. </bean>  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值