2011-01-11 12 views
15

Immagina un contesto di applicazione graduale con fasi diverse. Iniziamo con una fase iniziale per definire l'infrastruttura necessaria. I contesti dell'applicazione xml vengono caricati in sequenza.Come estendere liste e mappe già definite nel contesto di applicazione Spring?

Il motivo per suddividere questi file è un meccanismo di estensione/plugin.

Fase 01-default-configuration.xml

Prepariamo e dichiariamo la mappa con id exampleMapping per aumentare in un secondo momento con i dati.

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

fase(opzionale)

-configuration.xml 02-custom Abbiamo configurare il exampleMapping e aggiungere una voce.

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

Fase 03-make-uso-di-configuration.xml(obbligatorio)

Utilizza la mappa definita exampleMapping, se è configurato customly o è ancora la mappa dichiarata vuota.

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

Il problema qui è, che non è possibile aggiungere voci alla mappa exampleMapping dopo la prima fase. Spring lancia un'eccezione che la mappa con ID exampleMapping esiste già. Se lasciamo fuori la prima fase, la mappa non è dichiarata e il terzo stadio non può risolvere exampleMapping che produce anche un'eccezione.

Come posso risolvere questo problema? Ho letto Collection merging (documenti di primavera) ma questo non ha aiutato. È possibile aggiungere valori in seguito a mappe/elenchi prima di utilizzarli?

Grazie!

+0

Cosa intendi per "non può essere migliorato con le voci dopo la prima fase"? – skaffman

+0

@skaffman: Siamo spiacenti, ftfy. – codevour

risposta

10

È può definireexampleMapping è la seconda definizione è in un file separato, e si utilizza <import resource="..."/> di importare un file in un altro, ma è un approccio fragili e facilmente rotto.

Suggerisco una strategia più robusta. Sostituire il exampleMapping con una classe Registry, che a sua volta contiene e gestisce le mappature:

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

Quindi, scrivere una classe che registra una mappatura con il Registro di sistema:

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

La vostra configurazione diventa qualcosa di simile questo:

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

Queste mappature possono ora essere sparsi per tutta la configurazione in qualsiasi modo si vede in forma, e saranno auto-assemblaggio. ExampleServcie viene quindi iniettato con MappingRegistry ed estrae i mapping di conseguenza.

È un po 'più di lavoro di quello che hai già, ma è molto più flessibile e meno incline agli errori. Questo è particolarmente utile se stai cercando di costruire un framework estensibile di qualche tipo; vuoi mettere meno vincoli su come le persone lo usano.

+0

Ho già utilizzato questo meccanismo per fornire una funzionalità estensibile per un tipo speciale di filtro/valore-provider. Pensavo che questo sarebbe stato troppo costoso e c'è un modo più semplice per ottenere questo per elenchi/mappe. Apparentemente no. Lo implementerò come hai proposto tu. Forse vedremo una funzione simile in primavera in futuro? Grazie! – codevour

+0

@codevour: non penso che questo sia eccessivo, penso che questo sia un buon design. È più robusto, più esplicito e più leggibile rispetto all'uso di una mappa nuda, secondo me. – skaffman

+1

Forse hai ragione, questo potrebbe essere un modo molto generico per fare ciò, i fagioli potrebbero anche essere anonimi. Ho pensato un po 'a questo, questo si adatta bene al mio approccio. Grazie ancora. – codevour

13

Entrambi map e list hanno questo attributo denominato merge=true|false per unire due elenchi. In alternativa puoi utilizzare MethodInvokingFactoryBean per chiamare il metodo di aggiunta dell'elenco già definito per aggiungere altri elementi in un secondo momento.

Esaminiamo il vostro esempio.

1) Primo il secondo scenario con MethodInvokingFactoryBean. Invece di definire il modo in cui lo fai, ho definito i tuoi fagioli in modo leggermente diverso.

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

In un altro file di contenuto dell'applicazione, è possibile eseguire quanto segue per estendere la mappa.

<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) Ora il primo scenario. Per questo, ho appena trovato qualcosa a pagina 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> 

Speranza che aiuta.

+0

Puoi fornire un semplice esempio? Sfortunatamente non lo faccio funzionare. – codevour

+0

Ho aggiornato la risposta e ho dato un esempio per entrambe le opzioni. –

+0

Penso che il primo scenario non sia un grande progetto per questo approccio e suona molto sporco per me. Il secondo è un po 'offuscante, penso che scrivere un registro come menzionato da skaffmann sia il migliore. Grazie per la tua risposta e spiegazione! – codevour

Problemi correlati