web-dev-qa-db-ja.com

Springアプリケーションのコンテキストで定義済みのリストとマップを拡張するにはどうすればよいですか?

さまざまなフェーズを持つ段階的なアプリケーションコンテキストを想像してみてください。必要なインフラストラクチャを定義するための初期段階から始めます。 xmlアプリケーションコンテキストは順番にロードされます。

これらのファイルを分割する理由は、拡張機能/プラグインメカニズムです。

ステージ01-default-configuration.xml

後でデータで拡張するために、ID exampleMappingでマップを準備して宣言します。

<beans xmlns="http://www.springframework.org/schema/beans" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="[...]">

  <util:map id="exampleMapping" />
</beans>

ステージ02-custom-configuration.xml(オプション)

exampleMappingを構成し、エントリを追加します。

<beans xmlns="http://www.springframework.org/schema/beans" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="[...]">

  <util:map id="exampleMapping">
    <entry key="theKey" value="theValue" />
  </util:map>
</beans>

ステージ03-make-use-of-configuration.xml(必須)

定義されたマップexampleMappingを使用します。これは、カスタムで構成されているか、まだ空の宣言済みマップであるかは関係ありません。

<beans xmlns="http://www.springframework.org/schema/beans" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="[...]">

    <bean id="exampleService" class="com.stackoverflow.example.ExampleService">
       <property name="mapping" ref="exampleMapping" />
    </bean>
</beans>

ここでの問題は、最初の段階の後でexampleMappingマップにエントリを追加できないことです。 Springは、IDがexampleMappingのマップがすでに存在するという例外をスローします。最初のステージを省略すると、マップは宣言されておらず、3番目のステージはexampleMappingを解決できず、これも例外を生成します。

この問題を解決するにはどうすればよいですか? Collection merging (spring docs)を読みましたが、これは役に立ちませんでした。後でマップ/リストに値を追加してから使用することはできますか?

ありがとうございました!

16

あなたcan define exampleMappingは2番目の定義が別のファイルにあり、<import resource="..."/>を使用して1つのファイルを別のファイルにインポートしますが、これは壊れやすいアプローチであり、簡単に壊れます。

より堅牢な戦略を提案します。 exampleMappingRegistryクラスに置き換えます。このクラスには、マッピングが含まれ、管理されます。

public MappingRegistry<K,V> {

   private final Map<K,V> mappings = new HashMap<K,V>();

   public void addMapping(K key, V value) {
      mappings.put(key, value);
   }

   public Map<K,V> getMappings() {
      return Collections.unmodifiableMap(mappings);
   }
}

次に、マッピングをレジストリに登録するクラスを記述します。

public class MappingRegistrar<K,V> {

   private final MappingRegistry<K,V> registry;

   private K key;
   private V value;

   @Autowired
   public MappingRegistrar(MappingRegistry<K,V> registry) {
      this.registry = registry;
   }

   public void setKey(K key) {
      this.key = key;
   }

   public void setValue(V value) {
      this.value = value;
   }

   @PostConstruct
   public void registerMapping() {
      registry.addMapping(key, value);
   }
}

設定は次のようになります。

<bean id="mappingRegistry" class="com.xyz.MappingRegistry"/>

<bean id="mappingA" class="com.xyz.MappingRegistrar" p:key="keyA" p:value="valueA"/>
<bean id="mappingB" class="com.xyz.MappingRegistrar" p:key="keyB" p:value="valueB"/>
<bean id="mappingC" class="com.xyz.MappingRegistrar" p:key="keyC" p:value="valueC"/>

これらのマッピングは、適切と思われる方法で構成全体に分散させることができ、自己組織化されます。次に、ExampleServcieMappingRegistryが挿入され、それに応じてマッピングが抽出されます。

これは、すでに持っているものよりも少し手間がかかりますが、はるかに柔軟性があり、エラーが発生しにくくなっています。これは、ある種の拡張可能なフレームワークを構築しようとしている場合に特に役立ちます。あなたは人々がそれをどのように使うかについてより少ない制約を課したいのです。

12
skaffman

maplistの両方に、2つのリストをマージするためのmerge=true|falseという名前のこの属性があります。または、MethodInvokingFactoryBeanを使用して、定義済みのリストのaddメソッドを呼び出し、後でアイテムを追加することもできます。

あなたの例を見てみましょう。

1)最初にMethodInvokingFactoryBeanを使用した2番目のシナリオ。あなたのやり方を定義する代わりに、私はあなたのBeanを少し異なって定義しました。

<bean class="Java.util.HashMap" id="exampleMapping">
    <constructor-arg index="0">
        <map>
            <entry key="theKey" value="theValue"/>
        </map>
    </constructor-arg>
</bean>

<bean id="exampleService" class="com.stackoverflow.example.ExampleService">
    <property name="mapping" ref="exampleMapping"/>
</bean>

別のアプリケーションコンテンツファイルで、次の操作を実行してマップを拡張できます。

<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
    <property name="targetObject" ref="exampleMapping"/>
    <property name="targetMethod" value="putAll"/>
    <property name="arguments">
        <list>
            <map id="exampleMapping">
            <entry key="theKey2" value="theValue2"/>
            <entry key="theKey3" value="theValue3"/>
            <map>
        </list>
    </property>
</bean>

2)最初のシナリオです。これについては、ページで何かを見つけました http://forum.springsource.org/showthread.php?t=53358

<bean id="commonData" class="A">
    <property name="map">
        <util:map>
            <entry key="1" value="1"/>
        </util:map>
    </property>
</bean>
<bean id="data" class="A" parent="commonData">
    <property name="map">
        <util:map merge="true">
            <entry key="2" value="2"/>
        </util:map>
    </property>
</bean>

それが役に立てば幸い。

14
Umut Utkan