如何从配置了运行时参数的 Guice 中获取单例?

How can I a get singleton from Guice that's configured with runtime parameters?

我的总体目标是从属性文件加载属性,然后将这些属性注入到我的对象中。我还想使用这些属性使用 Guice 实例化某些单例 classes。我的单身人士 class 看起来像这样:

public class MainStore(){
  private String hostname;

  @Inject
  public void setHostname(@Named("hostname") String hostname){
    this.hostname = hostname;
  }

  public MainStore(){
    System.out.println(hostname);
  }
}

我正在尝试使用此提供程序实例化一个单例:

public class MainStoreProvider implements Provider<MainStore> {
  @Override
  public MainStore get(){
    MainStore mainStore = new MainStore();
    return mainStore;
  }
}

我的 configurationModule 是一个从运行时指定的 属性 文件加载配置的模块:

public class ConfigurationModule extends AbstractModule {
  @Override
  protected void configure(){
    Properties properties = loadProperties();
    Names.bindProperties(binder(), properties);
  }

  private static Properties loadProperties() {
    String resourceFileName = "example.properties";
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    InputStream inputStream = classLoader.getResourceAsStream(resourceFileName);

    Properties properties = new Properties();
    properties.load(inputStream);

    return properties;
  }
}

我的 example.properties 文件包含:

hostname = testHostName

然后当我需要我正在使用的 MainStore 单例时:

Injector injector = Guice.createInjector(new ConfigurationModule());
MainStoreProvider mainStoreProvider = injector.getInstance(MainStoreProvider.class);
MainStore mainStore = mainStoreProvider.get();  //MainClass singleton

这是正确的下坡路吗?我应该以完全不同的方式来做这件事吗?为什么我的 MainStore 没有打印出正确的主机名?

我写了一个小例子来演示如何绑定单例,如何注入 属性 等等。

public class TestModule extends AbstractModule {

    @Override
    protected void configure() {
        Properties p = new Properties();
        p.setProperty("my.test.string", "Some String"); // works with boolean, int, double ....
        Names.bindProperties(binder(),p);
        bind(X.class).to(Test.class).in(Singleton.class); // This is now a guice managed singleton
    }

    public interface X {

    }

    public static class Test implements X {

        private String test;

        @Inject
        public Test(@Named("my.test.string") String test) {
            this.test = test;
            System.out.println(this.test);
        }

        public String getTest() {
            return test;
        }
    }

    public static void main(String[] args) {

        Injector createInjector = Guice.createInjector(new TestModule());
        Test instance = createInjector.getInstance(Test.class);

    }
}

configure 方法现在负责告诉 guice 我的测试 class 是单例。

它打印出正确的主机名(属性 测试),因为我注入了构造函数并设置了 属性。

您也可以使用提供程序执行此操作,但是您必须手动创建对象。在我的例子中,这看起来像这样:

public static class TestProvider implements Provider<X> {

        private String test;

        private X instance;

        public TestProvider(@Named("my.test.string") String test) {
            this.test = test;
        }

        @Override
        public X get() {
            if(instance == null) {
                instance = new Test(test);
            }
            return instance;
        }

    }

绑定将如下所示:

bind(X.class).toProvider(TestProvider.class);

这是你想要的吗?

干杯,

亚瑟

编辑:

我做了一些测试,发现这个要注意:

您可以将提供商绑定为单例:

bind(X.class).toProvider(TestProvider.class).in(Singleton.class);

这样你就不需要自己处理单例创建了:

public static class TestProvider implements Provider<X> {

        private String test;

        private X instance;

        @Inject
        public TestProvider(@Named("my.test.string") String test) {
            this.test = test;
        }

        @Override
        public X get() {
            return instance;
        }

    }

以上代码将创建对象 X 的单例。