7

我有一个作为独立 java 应用程序的一部分运行的数据访问类。它目前正在工作,这意味着定义了事务管理器,但我想重构类以减少事务的范围,但如果我这样做了,我会得到org.hibernate.HibernateException: No Hibernate Session bound to thread,并且配置不允许在此处创建非事务性的,这意味着移动@Transactional以某种方式阻止了它被识别。

我的原始版本的重构方法是私有的,但我发现建议将其更改为公共,因为在某些情况下注释不会被拾取。

public class DoStuff {
    @Transactional
    public void originalMethod() {
        // do database stuff
        ...

        // do non-database stuff that is time consuming
        ...
    }
}

我想要做的是重构以下

public class DoStuff {
    public void originalMethod() {
        doDatabaseStuff()

        doNonDatabaseStuff()
    }

    @Transactional
    public void doDatabaseStuff() {
        ...
    }

    public void doNonDatabaseStuff() {
        ...
    }
}
4

2 回答 2

8

编辑:

你需要了解Spring 代理是如何工作的,才能理解为什么你的重构不起作用。

对象引用上的方法调用将是代理上的调用,因此代理将能够委托给与该特定方法调用相关的所有拦截器(建议)。但是,一旦调用最终到达目标对象,它可能对自身进行的任何方法调用都将针对 this 引用而不是代理调用。这具有重要意义。这意味着自调用不会导致与方法调用相关的建议有机会执行。

@Transactional 使用 Spring AOP,Spring 使用代理。这意味着当您从另一个类调用 @Transactional 方法时,Spring 将使用代理,因此将应用事务建议。但是,如果您从同一个类调用该方法,spring 将使用“this”引用而不是代理,因此不会应用事务建议。

原答案:

这是在类似情况下对我有用的方法。

public class DoStuff implement ApplicationContextAware {    
private ApplicationContext CONTEXT;
public void setApplicationContext(ApplicationContext context) throws BeansException {
    CONTEXT = context;
}

    public void originalMethod() {           
        getSpringProxy().doDatabaseStuff()              
        doNonDatabaseStuff()       
    }

    private DoStuff getSpringProxy() {
        return context.getBean(this.getClass());     
    } 
    @Transactional       
    public void doDatabaseStuff() {           
        ...       
    }          

    public void doNonDatabaseStuff() {           
        ...       
    }   
} 

解释:

  1. 创建类ApplicationContextAware,使其具有对上下文的引用
  2. 当您需要调用事务方法时,从上下文中获取实际的 spring 代理
  3. 使用此代理调用您的方法,以便实际应用 @Transactional。
于 2012-08-23T13:30:51.723 回答
0

您的方法看起来应该可以正常工作,我希望这个问题与 Spring 代理有关。

我询问接口的原因与 Spring 应用事务行为的默认方法有关 - JDK 动态代理。

如果您的班级的实际定义是:

public class DoStuff implements Doable {
  public void originalMethod() {

  }
}

public interface Doable {
  public void originalMethod();
}

如果这确实是结构,那么当您移动到新结构时,Spring 无法代理新doDatabaseStuff方法。

您解决此问题的选项:

  • 将新方法添加到您的接口以确保 Spring 可以代理它们
  • 转向使用基于 CGLIB 的代理(这些不依赖于接口)
于 2012-08-23T19:08:07.827 回答