需要模式建议(Hibernate + Guice)

时间:2008-12-16 20:42:22

标签: java dependency-injection design-patterns

我正在寻找有关如何将运行时依赖项注入从Hibernate检索的JPA实体的建议。我的问题基本上是这样的:

我有一些Transaction对象的不同子类。每个Transaction子类在执行时都有不同的行为,并且需要与环境不同的一组依赖项。这些Transaction对象由Hibernate作为JPA实体进行管理,因此我不能有效地使用Guice进行依赖注入来填充实例及其环境依赖性,就像我在其余的应用程序中一样。

为了解决这个问题,我采取了一种类似于访客模式的方法,如下所示:

public abstract class Transaction {
    // ...snip...
    public abstract void apply(Transactor transactor);
}

public class TransactionA extends Transaction {
    public void apply(Transactor transactor) {
        transactor.execute(this);
    }
}

public class TransactionB extends Transaction {
    public void apply(Transactor transactor) {
        transactor.execute(this);
    }
}
// other Transaction subclasses with the same boilerplate

public interface Transactor {
    public void execute(TransactionA trans);
    public void execute(TransactionB trans);
    // corresponding methods for other transaction types.
}

public class BeginTransactor {
     @Inject
     private Foo execAdep;
     public void execute(TransactionA trans) {
         execAdep.doSomething(...)    
     }

     @Inject
     private Bar execBdep;
     public void execute(TransactionB trans) {
         execBdep.doOther(...)    
     }
 }

我为事务生命周期的不同部分提供了各种Transactor实现。这些可以使用Guice依赖注入到我想要处理事务的上下文中,我只需调用它:

 Transactor transactor = injector.getInstance(BeginTransactor.class); //Guice injection
 Transaction t = ... //get a transaction instance
 t.apply(transactor);

我不喜欢这种方法是(1)并非每种类型的事务都应该在每个生命周期阶段都可执行,但每个Transactor必须为每个事务子类实现一个execute()方法。(2)基本上没有一个注入的依赖项用于处理多个事务类型。

基本上,我的Transactor界面&实现有很多不相关的crud在一起。理想情况下,我在事务对象本身上只有execute()方法,但我不希望调用代码必须知道事务的类型或它所需的依赖项。此外,这可能会使测试变得更难,因为如果它是Transaction对象的具体方法,我无法轻易地模拟出execute()方法。使用Transactor接口意味着我可以根据需要轻松模拟它。

任何人都可以建议如何以类型安全的方式解决这个问题,这种方式不会导致在Transactor中出现一堆大部分无关的行为,但保持可测试性并允许依赖注入吗?

2 个答案:

答案 0 :(得分:5)

我使用guice进行交易,但我使用AOP来执行它们。我几乎没有样板,牺牲了一点“魔力”。只要你截获的课程“在俱乐部”,它就能很好地运作。

class BusinessLogic {
    @Inject public EntityManager em;

    @Transactional
    publc void doSomething() {
       //...
       em.persist(myObj);
    }

    @Transactional
    public void doSomethingElse() {
       //...
       em.delete(myObj);
    }
}

class TransactionalInterceptor implements MethodInterceptor {
    @Inject static Injector injector;
    public Object intercept(MethodInvocation invocation) {
        EntityManager em = injector.getInstance(EntityManager.class);
        em.getTransaction().begin();
        Object result = invocation.proceed();
        em.getTransaction().commit();
        return result;
    }
}
class TransactionalModule extends AbstractModule {
    public void configure() {
        requestStaticInjection(TransactionalInterceptor.class);
        bindInterceptor(Matchers.any(), Matchers.annotatedWith(Transactional.class),
                 new TransactionalInterceptor());
    }
}

答案 1 :(得分:2)

检查:http://www.hibernate.org/hib_docs/v3/api/org/hibernate/Interceptor.html

将您的休眠配置为使用Intercetor。方法

public Object instantiate(String entityName,
                      EntityMode entityMode,
                      Serializable id)

将被调用

Instantiate the entity class. Return null to indicate that Hibernate
should use the default constructor of the class. The identifier property
of the returned instance should be initialized with the given identifier. 

您可以从那里调用您的injector.getInstance()。

也许,您可以使用getEntity()或onLoad()方法。从onLoad()方法中,您可以调用injector.injectMembers()。