Google Guice Properties Management

for*_*has 7 java dependency-injection properties guice

我想在java webapp中创建一个适当的属性管理策略,在google guice上作为DI框架进行中继.

我想有一个机制来回答以下3个要求:

  • 我希望能够使用guice注入属性(@Named)
  • 我希望能够以静态方式访问属性
  • 该机制应该支持属性的优先级,这意味着属性可以在已部署的战争中以特定值包装,但在目标系统级别或本地文件系统(我部署的目标机器)上也可能是冗余的,在这种情况下例如,战争中的值将被目标机器中存在的值覆盖.

我相信这是标准要求.现在,使用guice标准活页夹我可以轻松获得第一个要求,但不能获得其他两个要求.为了获得另外两个,我创建了自己的类来执行以下操作:

  • 包装并公开guice的绑定方法(绑定属性的绑定方法)例如:

public static void bindString(AnnotatedBindingBuilder<String> binder, String property, String defaultValue) { binder.annotatedWith(Names.named(property)).toInstance(getProperty(property, defaultValue)); }

getProperty方法知道如何处理我的属性(从war或系统级别获取值)并静态公开属性.

所以基本上只要我使用我为属性绑定创建的这个实用程序我很好,它涵盖了我的所有要求但是一旦我使用标准的guice绑定,我就失去了第二和第三个要求.

有没有办法覆盖guice绑定并获得所有这3个要求?

一旦我在一个基于弹簧的应用程序中有相同的挑战,并且很容易.我使用以下方法实现了ApplicationContextInitializer:

@Override public void initialize(ConfigurableWebApplicationContext ctx) { PropertySource<Map<String, Object>> localProps = null; try { localProps = new ResourcePropertySource(new ClassPathResource(LOCAL_PROPERTIES_FILE_NAME)); } catch (IOException e) { LOG.fatal("Could not load local properties from classpath " + LOCAL_PROPERTIES_FILE_NAME); return; } LOG.info("Loaded configuration from classpath local file " + LOCAL_PROPERTIES_FILE_NAME); ctx.getEnvironment().getPropertySources().addFirst(localProps); }

所以这给了我一种方法来为我的环境添加具有最高优先级的本地属性.如果与war属性重叠,则本地属性具有更高的优先级.另外,我静态地暴露了我的环境,因此我可以静态访问我的属性(对于不是由容器管理的服务,主要是遗留的).

我如何用guice实现这一目标?

Nat*_*mer 1

不幸的是,我认为您不会找到任何可以为您提供真正干净且令人满意的实现的东西。特别是,我认为如果您不亲自实现至少部分内容,您将无法找到任何能够准确满足您需求的东西。

如果我有这些需求,我会确保我的注入器是在中央 InjectorFactory 中创建的。如果您需要来自外部的大量参数来创建注入器,我只需在应用程序的一开始就创建一次,然后将注入器缓存到静态最终字段中。这将使其可用于静态方法。我会将我的“后备”属性加载绑定到显式提供程序。这样,我将不使用标准的 Names.bindProperties(...) 方法,而是将其直接绑定到 Provider。然后,该提供程序实现执行回退或合并多个属性文件所需的逻辑。将注入器缓存到静态字段意味着我可以调用静态方法来从注入类之外的全局上下文访问属性。

使用您自己的提供商最初看起来并不令人愉快,但可以提供一些额外的好处。对于初学者来说,您可以按照自己的意愿实施后备策略。此外,您可以添加其他行为,例如自动重新加载属性文件等(我的代码示例中未显示)。

public class InjectorFactory {
    private static Injector injector = null;
    public static synchronized Injector getOrCreateInjector() {
        if(injector == null) {
            injector = Guice.createInjector(new AbstractModule() {
                @Override
                protected void configure() {
                    Properties properties1 = createProperties("file1.properties");
                    Properties properties2 = createProperties("file2.properties");
                    Set<Object> propertyNames = new HashSet<Object>();
                    propertyNames.addAll(properties1.keySet());
                    propertyNames.addAll(properties2.keySet());

                    for (Object object : propertyNames) {
                        String propertyName = (String) object;
                        bind(String.class).annotatedWith(Names.named(propertyName)).toProvider(new StringProvider(properties1, properties2, propertyName));
                    }
                }

                private Properties createProperties(String propertyFileName) {
                    try {
                        InputStream stream = InjectorFactory.class.getResourceAsStream(propertyFileName);
                        try {
                            Properties properties = new Properties();
                            properties.load(stream);
                            return properties;
                        } finally {
                            stream.close();
                        }

                    } catch (IOException exception) {
                        throw new RuntimeException("Could not load properties file");
                    }
                }
            });
        }
        return injector;
    }

    public static String getProperty(String propertyName) {
        return getOrCreateInjector().getInstance(Key.get(String.class, Names.named(propertyName)));
    }

}
Run Code Online (Sandbox Code Playgroud)

鉴于上述代码和 file1.properties:

property1=Property1Value
property2=Property2Value
Run Code Online (Sandbox Code Playgroud)

和文件属性:

property2=IncorrectProperty2Value
property3=Property3Value
Run Code Online (Sandbox Code Playgroud)

与提供者

public class StringProvider implements Provider<String> {
    private Properties properties1;
    private Properties properties2;
    private String propertyName;
    public StringProvider(Properties properties1, Properties properties2,
            String propertyName) {
        this.properties1 = properties1;
        this.properties2 = properties2;
        this.propertyName = propertyName;
    }
    public String get() {
        if(properties1.containsKey(propertyName)) {
            return properties1.getProperty(propertyName);
        }
        return properties2.getProperty(propertyName);
    }
}
Run Code Online (Sandbox Code Playgroud)

用法如下:

public class InjectorFactoryTest {
    public static void main(String ... parameters) {
        System.out.println(InjectorFactory.getProperty("property1"));
        System.out.println(InjectorFactory.getProperty("property2"));
        System.out.println(InjectorFactory.getProperty("property3"));
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

Property1Value
Property2Value
Property3Value
Run Code Online (Sandbox Code Playgroud)