在运行时更改Guice绑定

Nep*_*oxx 12 java architecture dependency-injection guice playframework-2.0

我希望能够在运行时更改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
    }
}
Run Code Online (Sandbox Code Playgroud)

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

更新 这是我目前提出的,它不像我想的那么漂亮,所以我仍然在寻找反馈

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);
Run Code Online (Sandbox Code Playgroud)

Jef*_*ica 14

你的版本几乎是完美的:你需要注入一些基于你编写的代码返回一个或另一个的对象,并且不需要辅助注入或类似的东西.也就是说,你可以跳过一些样板:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,你的MyClass将完全相同.这里Provider是单方法通用接口com.google.inject.Provider,或它扩展的等效内置javax.inject.Provider.在相关的Guice wiki主题上阅读有关Guice Providers的更多信息.

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

基本上,如果你绑定一个键Foo(一个类,提供者,@Provides方法或实例),你会自动注入一个FooProvider<Foo>没有额外的工作.get如果这是您想要的,那么提供商也是确保每次调用都能获得新实例的好方法; 使用原始版本,对于您注入的任何给定DomainProvider,您将始终获得相同的EarthDao或MarsDao实例.(如果你有像@Singleton这样的范围绑定,Guice也会尊重它;提供者只是让Guice参与进来,而不是重用一个普通的旧Java引用.)

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

  • @PapaSierra 如果你在 Guice 中配置它,那就是你会得到的;无论如何,你都会希望在 Guice 中以这种方式配置它,这样你就无法从另一个消费者那里获得不同的实例。我的观点是,通过注入单个实例而不是 Provider,无论 Guice 中的配置如何,您都将始终返回该实例。 (2认同)