【问题标题】:How can I a get singleton from Guice that's configured with runtime parameters?如何从配置了运行时参数的 Guice 获取单例?
【发布时间】:2016-07-26 12:29:07
【问题描述】:

我的总体目标是从属性文件加载属性,然后将这些属性注入到我的对象中。我还想使用这些属性来使用 Guice 实例化某些单例类。我的单例类如下所示:

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 没有打印出正确的主机名?

【问题讨论】:

  • 您的提供商未设置主机名属性。为什么首先需要提供者?由于 guice 没有处理你的类构造,它不会设置属性。您必须将您的属性注入您的提供程序实现并在创建后设置它。另外,如果我错了,请纠正我,但是您的 Provider 不会创建单例,而是每次都创建一个新实例。
  • 我想使用提供者为我提供单例。我不知道该怎么做,所以我可能做错了。我还认为 Guice 会将我的 Injected 属性级联到提供程序中创建的所有类中。不过,这种方法可能是完全错误的。
  • 不,guice 不会为您这样做,因为您是创建对象的人,而不是 guice。您的主机名打印错误的原因也是因为您在构造函数中打印它,但是您注入了一个方法。方法注入只能在构造对象之后发生(因为没有方法可以调用)。如果你想要一个单例,那么你可以在单例范围内绑定,并简单地用属性注入构造函数(就像你做方法一样)。将您的属性视为一个命名字符串对象,您可以将其注入到任何 Guice 托管类中。

标签: java dependency-injection singleton guice properties-file


【解决方案1】:

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

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 我的 Test 类是一个单例。

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

您也可以使用提供者来执行此操作,但是您必须手动创建对象。就我而言,这看起来像这样:

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 的单例。

【讨论】:

  • 这绝对有帮助!谢谢!有什么方法可以从 TestModule 中删除 Singleton.class 并将其放入 TestProvider 中。我想在我从属性文件中注入属性的任何地方使用 TestModule,并且在我需要它们的地方有不同的提供者。
  • 我不确定我是否理解。您可以将提供程序绑定为单例,然后它将返回第一次创建的相同实例。有关详细信息,请参阅我的更新。不同的供应商是什么意思?您是否使用相同的模块创建多个 Injector?
  • 这回答了我认为的问题。非常感谢您的帮助。最后一点,如果我不必将我的 TestProvider 耦合到 TestModule,那就太好了。
  • 您必须在某个地方绑定提供程序,否则 guice 将不知道如何处理它。您可以将您的 TestProvider 拆分为一个独立的单独模块,如果您愿意,可以将其安装在父模块中
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多