2015-11-03 8 views
7

DIフレームワークとしてgoogle guiceを中継するJava Webアプリケーションで適切なプロパティ管理戦略を作成したいと考えています。Google Guiceのプロパティ管理

Iは、以下の3つの要件応答機構がしたい:私はプロパティにアクセスできるようにしたいGuiceの(@Named)

  • を使用してプロパティを注入できるようにしたい

    • このメカニズムはプロパティの優先順位付けをサポートする必要があります。つまり、ある値でプロパティを展開された戦闘でラップすることができますが、ターゲットシステムレベルまたはローカルファイルシステム(ターゲットマシン私は展開します)、そのような場合には、戦争の価値は値thによって上書きされますatはターゲットマシンに存在します。

    これは標準的な要件です。今、Guiceの標準バインダーを使用して、私は簡単に最初の要件を得ることができますが、他の2つはありません。

    • ラップをし、例えばGuiceのの結合メソッド(プロパティを結合したもの)を公開:

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

    他の二つを取得するには、私は次のことを行い自分のクラスを作成しましたgetPropertyメソッドは、プロパティを処理する方法(戦争またはシステムレベルから値を取得する方法)を知っており、プロパティも静的に公開しています。

    プロパティバインディングのために作成したこのユーティリティを使用している限り、基本的にすべての要件がカバーされますが、標準のguiceバインディングを使用すると、2番目と3番目の要件が失われます。

    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); }

    をので、これは私に私の環境に最も高い優先順位を持つローカルのプロパティを追加する方法を与えました。戦争の特性と重複した場合には、地方のものが優先された。さらに私は自分の環境を静的に公開しているので、プロパティ(コンテナによって管理されていないサービス、ほとんどはレガシー)に対して静的にアクセスできます。

    私はguiceでこれをどのように達成できますか?

  • +0

    あなたは、静的な方法でプロパティにアクセスすることを、何を意味するのですか?つまり、特定のプロパティにアクセスするユースケースがありますが、アクセスするプロパティを実行時まで知ることはできませんか? –

    +0

    私は次のようなものを使ってプロパティにアクセスしたいと思っています:SomeClass.getProperty( "my.property");これはguiceによって管理されていないクラス(通常はレガシー)で、まだ自分のプロパティへのアクセスが必要な場合に便利です – forhas

    答えて

    1

    残念ながら、私はあなたが本当にきれいで満足できる実装を提供するものを見つけるつもりはないと思います。特に、私は、あなたが自分自身の少なくとも一部を自分で実装せずに、あなたが望むものを正確に与えるものを見つけることはないと思います。

    私がこれらのニーズを持っていたなら、インジェクターが中央のInjectorFactoryで作成されていることを確認しました。インジェクタを作成するために外部から多数のパラメータが必要な場合は、アプリケーションの最初の段階で一度作成し、インジェクタを静的な最終フィールドにキャッシュするだけです。これにより、静的メソッドを使用できるようになります。私は明示的なプロバイダに私の "フォールバック"プロパティロードをバインドします。その方法は、標準のNames.bindProperties(...を使用する代わりに)メソッドでは、プロバイダに直接バインドします。このプロバイダは、フォールバックの実行や複数のプロパティファイルのマージに必要なロジックを実装します。インジェクタを静的フィールドにキャッシュすると、注入されたクラス以外のグローバルコンテキストからプロパティにアクセスする静的メソッドを呼び出すことができます。

    独自のプロバイダを使用することは当初不愉快だったようですが、いくつかの追加の利点があります。まず、フォールバック戦略を自分の望む通りに実装することができます。さらに、プロパティファイルの自動再読み込みなどの動作を追加することもできます(コードサンプルには記載されていません)。

    :プロバイダ

    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); 
        } 
    } 
    

    次使用に

    property2=IncorrectProperty2Value 
    property3=Property3Value 
    

    property1=Property1Value 
    property2=Property2Value 
    

    そしてfile.properties:上記のコードとfile1.propertiesを考えると

    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))); 
        } 
    
    } 
    

    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")); 
        } 
    } 
    

    出力:

    Property1Value 
    Property2Value 
    Property3Value 
    
    +0

    ニースですが、まだ不利な点があります - 誰もこのファクトリの使用を強制しません。たとえば、誰かが新しいモデルを作成してこのファクトリを使用しないと決めた場合、guice管理されたプロパティと工場のプロパティとの間にギャップが生じることがあります。私は、誰もがモデル作成のために工場を使用する限り、これは解決策を提供することに同意しますが、これは私にとっては十分ではありません(私は現時点では似たような解決策を持っています、少しだけ簡単です:)) – forhas

    +0

    もちろんあります。一般に、誰かが新しいオブジェクトを作成したい場合は、新しいオブジェクトを作成することができます。そうでない場合は、独自のモジュールリストを作成する場合に独自のインジェクタを作成できます。 JVMポリシーの管理を開始し、どのクラスが他のどのクラスにアクセスすることが許可されていない限り、これを禁止する方法はありません。目標は、人々が「正しい方法」を行うことを可能な限り簡単にすることです。私が前に言ったように、私はあなたが非常に満足している解決策を見つけることはまずないと思う。 –