【问题标题】:Pattern suggestions needed (Hibernate + Guice)需要模式建议(Hibernate + Guice)
【发布时间】:2008-12-16 20:42:22
【问题描述】:

我正在寻找有关如何将运行时依赖项注入从 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() 方法,但我不希望调用代码必须知道事务的类型或它所需的依赖项。此外,这可能会使测试变得更加困难,因为如果 execute() 方法是 Transaction 对象上的具体方法,我将无法轻松模拟它。使用 Transactor 接口意味着我可以根据需要轻松地对其进行模拟。

谁能建议如何以类型安全的方式解决这个问题,这种方式不会导致一堆大部分不相关的行为在 Transactor 中聚集在一起,但保持可测试性并允许依赖注入?

【问题讨论】:

    标签: java dependency-injection design-patterns


    【解决方案1】:

    我使用 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());
        }
    }
    

    【讨论】:

    • 太好了,谢谢!我曾怀疑可能有解决此问题的 AOP 方法,但不确定如何解决。
    【解决方案2】:

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

    将您的休眠配置为使用拦截器。方法

    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()。

    【讨论】:

      猜你喜欢
      • 2013-11-26
      • 2015-08-29
      • 2014-03-30
      • 1970-01-01
      • 2014-01-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多