Как расширить уже определенные списки и карты в контексте приложения Spring? - PullRequest
16 голосов
/ 11 января 2011

Представьте себе поэтапный контекст приложения с разными фазами.Мы начнем с раннего этапа определения необходимой инфраструктуры.Контексты приложения xml загружаются последовательно.

Причиной разделения этих файлов является механизм расширения / плагина.

Этап 01-default-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" />
</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 выдает исключение, что карта с идентификатором exampleMapping уже существует.Если мы пропустим первый этап, то карта не будет объявлена, а третий этап не сможет разрешить exampleMapping, что также приводит к исключению.

Как я могу решить эту проблему?Я прочитал Слияние коллекций (весенние документы), но это не помогло.Можно ли добавить значения позже в карты / списки перед их использованием?

Спасибо!

Ответы [ 2 ]

13 голосов
/ 11 января 2011

Оба map и list имеют этот атрибут с именем merge=true|false для объединения двух списков. В качестве альтернативы вы можете использовать MethodInvokingFactoryBean для вызова метода добавления уже определенного списка, чтобы добавить дополнительные элементы позже.

Давайте рассмотрим ваш пример.

1) Сначала второй сценарий с MethodInvokingFactoryBean. Вместо того, чтобы определять, как вы, я определил ваши бобы немного по-другому.

<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>

Надеюсь, это поможет.

12 голосов
/ 11 января 2011

Вы можете определить exampleMapping - второе определение находится в отдельном файле, и вы используете <import resource="..."/> для импорта одного файла в другой, но это хрупкий подход, который легко сломать.

Я предлагаю более надежную стратегию. Замените exampleMapping классом Registry, который, в свою очередь, содержит и управляет отображениями:

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"/>

Эти сопоставления теперь могут быть разбросаны по всей конфигурации любым удобным для вас способом, и они будут собираться самостоятельно. ExampleServcie затем вводится с помощью MappingRegistry и соответственно извлекает сопоставления.

Это немного больше работы, чем у вас уже есть, но гораздо более гибко и менее подвержено ошибкам. Это особенно ценно, если вы пытаетесь создать какую-то расширяемую структуру; Вы хотите наложить меньше ограничений на то, как люди его используют.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...