4

Guiceを使用し、構成ファイルからいくつかの構成設定を読み込むアプリケーションがあります。私はそれをこのようにロードします:

@Provides @Singleton
Settings provideSettings() {
    // code that loads the settings
}

特定のオブジェクトには特定の設定が必要であり、他のオブジェクトには他の設定が必要です。これらをコンストラクターに渡すのは理にかなっているように思えますが、最終的には次のような多くの定型コードになります。

@Provides @Named("integerValueSetting1")
int provideIntegerValueSetting1(Settings settings) {
    return settings.getInteger("integerValueSetting1");
}

@Providesそして、すべての設定タイプに対してそのようなメソッドを作成する必要があります。さらに、コンストラクターで適切な設定に注釈を付ける必要があります。そのようです:

@Inject
public MyClass(@Assisted String objectName, @Named("integerValueSetting1") myValue) {
    // blah blah constructor
}

これは私をあまり救わないようです!さらに悪いことに、設定パラメータごとにカスタムアノテーションクラスを作成した場合です。もっと良い方法が必要ですよね?

1つの解決策は、Settingsオブジェクトを直接渡すことですが、これはベストプラクティスに違反します。直接の依存関係のみを挿入します...

4

2 に答える 2

2

大量の設定があり、それぞれに新しいバインディングアノテーションを作成したくない場合は、それらを列挙型に配置し、その列挙型を共通のバインディングアノテーションで使用してみてください。これは解決策が少し複雑かもしれませんが、避けようとしている定型文を節約することもできます。

このようにして、文字列(低速で脆弱)の代わりにオブジェクト参照(IDE対応)を照合し、それでも1つのバインディングアノテーションのみを作成できます。

public enum Config {
  DB_NAME("db_name"),
  DB_HOST("db_host_name_specified_in_file"),
  SOME_NUMBER("some_number"),
  ;

  private final String propertyName;

  private Config(String propertyName) {
    this.propertyName = propertyName;
  }

  public String getPropertyName() {
    return propertyName;
  }

  public InjectConfig annotation() {
    // Create an implementation of InjectConfig for ease of binding.
    return new InjectConfig() {
      @Override public Class<? extends Annotation> annotationType() {
        return InjectConfig.class;
      }

      @Override public Config value() {
        return Config.this;
      }

      @Override public boolean equals(Object obj) {
        if (obj == this) {
          return true;
        } else if (!(obj instanceof InjectConfig)) {
          return false;
        }
        return value() == ((InjectConfig) obj).value();
      }

      /** @see Annotation#hashCode */
      @Override public int hashCode() {
        return (127 * "value".hashCode()) ^ value().hashCode();
      }
    };
  }

  @Retention(RetentionPolicy.RUNTIME)
  @BindingAnnotation
  public static @interface InjectConfig {
    Config value();
  }
}

これで、ループ内でそれぞれを繰り返してバインドできます。

public class YourModule extend AbstractModule {
  @Override public void configure() {
    // You can get a Provider in a Module as long as you
    // don't call get() before the injector exists.
    Provider<Settings> settingsProvider = binder().getProvider(Settings.class);
    for (Config config : Config.values()) {
      String propertyName = config.getPropertyName();
      // Guice's TypeConverter will convert Strings to the right type.
      bind(String.class).annotatedWith(config.annotation()).toProvider(
          new GetValueFromSettingsProvider(settingsProvider, propertyName));
    }
  }
}

そして、必要なものだけを直接注入します。

/** Your constructor */
YourClass(@InjectConfig(DB_USER) String user,
    @InjectConfig(SOME_NUMBER) int number) { }

これをテストする機会はありませんでしたが、私が知る限り、うまくいくはずです。特定の設定のユースケースを考えると、作成したものをマッサージするGetValueFromSettingsProvidergetConfigValueFromSettings、列挙型にオーバーライド可能なメソッドを作成する必要がある場合があります。ただし、何らかの方法で(列挙型キー、ファイル内のプロパティ名、プロパティタイプ)タプルを格納する必要があることを忘れないでください。プログラムで管理するには、列挙型が最適な方法のようです。

于 2013-03-04T16:12:41.213 に答える
0

Tadeonプロジェクト、特にその構成機能を確認してください。

次の.propertiesファイルで:

foo=foo
list=1, 2, 3

およびコンポーネント:

public class PropertyInjectedComponent {

    private final String foo;
    private final List<String> list;

    @Inject
    public PropertyInjectedComponent(
        @Named("foo") String foo,
        @Named("list") List<String> list) {
    this.foo = foo;
    this.list = list;
    }
    ...
}

名前付き値のプロパティをスキャンするようにguiceモジュールを設定するだけです。

Injector injector = Guice.createInjector(new AbstractModule() {
        protected void configure() {
            GuiceConfigurations.bindProperties(binder(), new File("src/test/data"), "conf1.properties");
        }
    });

これがテストファイルです。

Settingsクラスをapache Configuration、JDKに置き換えるか、PropertiesTadeonMap<String, String>ソースを調査して、クラスに同様のソリューションを作成することができますSettings

于 2013-02-28T22:27:53.487 に答える