`
fyting
  • 浏览: 217109 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

SpringAOP嵌套调用的解决办法

    博客分类:
  • Java
阅读更多
Spring AOP在同一个类里自身方法相互调用时无法拦截。比如下面的代码:
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
分享到:
评论
5 楼 greatwqs 2016-08-31  
标记一下   
4 楼 lengyubing 2013-06-03  
非常感谢,刚好遇到这个问题
3 楼 fyting 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>

这里有一份源码,就是用那个帖子修改的。
2 楼 popi 2007-08-06  
楼主搞的这个解决办法比较有意思。
1 楼 yeshucheng 2007-08-06  
请问能否以这个帖子的例子来具体说明呢?
http://www.iteye.com/post/347986

相关推荐

    AOP实现自我调用的事物嵌套问题

    当我们遇到"AOP实现自我调用的事物嵌套问题"时,这通常涉及到Spring框架中的事务管理,特别是自调用方法在事务处理时可能会引发的问题。 首先,让我们理解Spring AOP的事务管理是如何工作的。Spring使用代理模式来...

    spring-nested-aop.zip_aop_spring aop

    在这个名为"spring-nested-aop.zip"的压缩包中,包含了一个关于Spring AOP的嵌套事务处理的示例程序。这通常涉及到在一个事务方法内部调用另一个事务方法的情况,两个方法都需要在同一事务范围内运行,以便确保数据...

    Spring AOP之基于Schema配置总结与案例

    通过这样的配置,当UserService的任何方法被调用时,都会自动触发LoggingService中的对应通知方法,无需在UserService中做任何修改,这就是Spring AOP的声明式特性。 在实际开发中,我们可以根据需求定义多个切面,...

    Spring AOP管理Hibernate事务(TransactionInSpringAOP)

    3. **AOP代理**:Spring会创建一个代理对象来包围业务逻辑,当方法调用时,代理会检查是否有@Transactional注解,并根据注解的属性启动一个新的事务或参与到现有的事务中。 4. **事务传播行为**:比如PROPAGATION_...

    springAop事务配置

    - 事务的边界通常由方法定义,事务管理器会在方法调用开始时开始事务,在方法正常结束时提交事务,遇到异常时回滚事务。 - 对于异步方法,需要额外注意事务边界,因为线程切换可能导致事务管理失效,可能需要使用...

    SSM整合-用springaop-demo01实现了注解AOP,SSM-MybatisOneForOne-demo01实现了

    在这个场景下,我们有两个演示项目:`springaop-demo01` 和 `SSM-MybatisOneForOne-demo01`。 1. **Spring AOP(面向切面编程)**: - **AOP概念**:AOP是Spring框架的核心特性之一,它允许程序员定义“切面”,...

    spring隔离级别和aop基础.md

    在 AOP 中,反射常用于创建代理类和调用目标方法。虽然反射提供了强大的灵活性,但它可能会影响性能,尤其是在大量使用的情况下。 综上所述,事务隔离级别和事务传播行为是确保数据一致性和事务处理的重要工具,而 ...

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

    总结来说,通过结合Spring AOP和Redis,实现了报表计算结果的高效缓存,有效解决了报表加载慢的问题,降低了系统在高并发情况下的压力,提高了用户体验。同时,这种缓存策略也降低了对源代码的侵入性,使得系统维护...

    spring学习文档

    所谓的传播性,是指:当 service 层的方法发生互相调用时,事务将如何在互相嵌套的 service 之间传递的概念。常见的事务类型有 PROPAGATION_REQUIRED、PROPAGATION_SUPPORTS、PROPAGATION_MANDATORY、PROPAGATION_...

    Spring2.x对事务的管理(AOP)

    编程式事务管理需要在代码中显式调用开始、提交或回滚事务的方法,而声明式事务管理则通过注解或XML配置,让Spring自动处理事务。 **二、Spring的AOP(面向切面编程)** AOP允许程序员定义“切面”,这些切面可以...

    Spring学习思维笔记.pdf

    Spring框架具有高度的集成性和灵活性,它通过IoC和AOP解决了企业级应用开发中的一些常见问题,如代码冗余、依赖关系难以管理、横切关注点难以维护等问题。Spring的事务管理支持和动态代理机制,使得开发者可以更加...

    spring 事务管理的理解

    我们可以使用@Transactional注解在方法级别声明事务,Spring AOP代理会在方法执行前后自动处理事务的开始、提交或回滚。声明式事务管理更加简洁,易于维护。 接下来,我们来看看Spring如何配置事务管理。在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事务总结.docx

    - **问题分析**:当事务方法被声明为非`public`时,Spring的AOP代理将无法正确识别这些方法,从而导致事务配置无效。 - **示例代码**: ```java @Service public class UserService { @Transactional private...

    Spring中文帮助文档

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

    springtransaction 事务管理

    声明式事务管理依赖于Spring的AOP机制,它可以在不修改业务代码的情况下,通过代理对象拦截方法调用,实现事务的自动控制。AOP允许我们定义“切面”(Aspect),即关注点的模块化,如事务管理就是一个典型的横切...

    cglib 动态代理

    7. **应用场景**:CGLib在Spring AOP、MyBatis等框架中都有广泛的应用,例如在Spring中,如果目标对象没有实现接口,Spring就会使用CGLib来创建代理。 8. **与JDK动态代理的区别**:JDK的动态代理基于接口,而CGLib...

    Spring 开发参考手册

    - **拦截器** (Interceptor): 在方法调用前后执行的逻辑。 - **AOP 代理** (AOP Proxy): 实现 AOP 的关键,Spring 使用 JDK 动态代理或 CGLIB 代理来实现。 #### 六、属性编辑器、数据绑定、校验与 BeanWrapper ...

Global site tag (gtag.js) - Google Analytics