【问题标题】:Changing Guice bindings at runtime在运行时更改 Guice 绑定
【发布时间】:2014-07-26 21:21:59
【问题描述】:

我希望能够在运行时更改 Guice 注入,以支持基于用户输入的多次注入。这就是我想要实现的目标:

public interface IDao {
    public int someMethod();
}

public class DaoEarth implements IDao {
    @Override
    public int someMethod(){ ... }
}

public class DaoMars implements IDao {
    @Override
    public int someMethod(){ ... }
}

public class MyClass {
    @Inject
    private IDao myDao;

    public int myMethod(String domain) {
        //If Domain == Earth, myDao should be of the type DaoEarth

        //If Domain == DaoMars, myDao should be of the type DaoMars
    }
}

我正在考虑编写自己的提供程序,但我不知道如何使用该提供程序在运行时更改我的绑定。欢迎和赞赏任何意见:)!

更新 这是我目前想出的,它没有我想要的那么漂亮,所以我还在寻找反馈

public class DomainProvider {
    @Inject @Earth
    private IDaoProvider earthDaoProvider;

    @Inject @Mars
    private IDaoProvider marsDaoProvider;

    public IDaoProvider get(Domain domain){
        switch (domain){
            case EARTH:
                return earthDaoProvider;
            case MARS:
                return marsDaoProvider;
        }
    }

    public IDaoProvider get(String domain){
        Domain parsedDomain = Domain.valueOf(domain.toUpperCase());
        return get(parsedDomain);
    }
}

//MarsDaoProvider would be equivalent
public class EarthDaoProvider implements IDaoProvider {
    @Inject @Earth
    private IDao earthDao;

    public IDao getDao() {
        return earthDao;
    }
}

// This means that in "MyClass", I can do:
public class MyClass {
    @Inject
    private DomainProvider domainProvider;

    public int myMethod(String domain) {
        IDaoProvider daoProvider = domainProvider.get(domain);
        IDao dao = daoProvider.getDao();

        //Now "dao" will be of the correct type based on the domain
    }
}

//Of course elsewhere I have the bindings set like
bind(IDao.class).annotatedWith(Earth.class).to(EarthDao.class);

【问题讨论】:

  • 我不确定辅助注射对我有什么帮助。我知道辅助注入允许您将注入的参数与提供的参数结合起来,但我不确定这将如何适用于我的情况。

标签: java architecture dependency-injection playframework-2.0 guice


【解决方案1】:

您的版本几乎是完美的:您将需要注入某种对象,该对象会根据您编写的代码返回一个或另一个,并且不需要辅助注入或类似的东西。也就是说,您可以跳过一些样板:

public class DomainProvider {
    // Just inject Providers directly without binding them explicitly.
    @Inject @Earth Provider<IDao> earthDaoProvider;
    @Inject @Mars Provider<IDao> marsDaoProvider;

    public Provider<IDao> get(Domain domain){
        switch (domain){
            case EARTH:
                return earthDaoProvider;
            case MARS:
                return marsDaoProvider;
        }
    }

    public Provider<IDao> get(String domain){
        Domain parsedDomain = Domain.valueOf(domain.toUpperCase());
        return get(parsedDomain);
    }
}
    

在这种情况下,您的 MyClass 将完全相同。这里,Provider 要么是单一方法的通用接口 com.google.inject.Provider,要么是它扩展的等效内置 javax.inject.Provider。在the relevant Guice wiki topic 上阅读有关 Guice 提供程序的更多信息。

bind(IDao.class).annotatedWith(Earth.class).to(EarthDao.class);
// You can now inject "@Earth IDao" and also "@Earth Provider<IDao>".

基本上,如果你绑定一个键 Foo(到一个类、提供者、@Provides 方法或实例),你 automatically get to inject either a Foo or Provider&lt;Foo&gt; 没有额外的工作。提供程序也是确保每次调用 get 时都能获得新实例的好方法,如果这是您想要的;使用您的原始版本,您将始终为您注入的任何给定 DomainProvider 获得相同的 EarthDao 或 MarsDao 实例。 (如果您有像 @Singleton 这样的范围绑定,Guice 也会尊重这一点;Provider 只是让 Guice 参与其中,而不是重用普通的旧 Java 引用。)

这意味着您可以跳过您的自定义 EarthDaoProvider 和 MarsDaoProvider,除非您确实需要对它们执行任何外部初始化——此时您最好调用bind(EarthDao.class).toProvider(EarthDaoProvider.class),这样在直接注入 EarthDao 时也会进行准备工作。您也可以通过在适当的 Provider 上调用 get 直接让 DomainProvider 返回一个 IDao 实例,并确保它每次都是一个新实例。

【讨论】:

  • 这是一个很好的答案,解释/链接的奖励积分:)
  • “提供程序也是确保每次调用 get 时都获得一个新实例的好方法” - 如果我想在每次调用时获取一个单例实例而不是一个新实例怎么办? get?
  • @PapaSierra 如果你在 Guice 中配置它,你会得到它;无论如何,您都希望它在 Guice 中以这种方式配置,因此您无法从另一个消费者那里获得不同的实例。我的观点是,通过注入单个实例而不是 Provider,无论 Guice 中的配置如何,您都将始终返回该实例。
猜你喜欢
  • 2017-09-23
  • 1970-01-01
  • 1970-01-01
  • 2012-06-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-31
  • 1970-01-01
相关资源
最近更新 更多