2009-09-28 4 views
11

Ho due file XML che definiscono i fagioli per la Spring Framework (versione 2.5.x):Come modificare bean definiti in un contenitore di primavera

containerBase.xml: 
<beans> 
    <bean id="codebase" class="com.example.CodeBase"> 
     <property name="sourceCodeLocations"> 
      <list> 
       <value>src/handmade/productive</value> 
      </list> 
     </property> 
    </bean> 
</beans> 

... e

containerSpecial.xml: 
<beans> 
    <import resource="containerBase.xml" /> 
</beans> 

Ora Voglio regolare la proprietà sourceCodeLocations di bean codebase entro containerSpecial.xml. Devo aggiungere un secondo valore src/generated/productive.

Un approccio semplice è quello di sostituire la definizione di codebase in containerSpecial.xml e aggiungere entrambi i valori, l'uno dall'altro containerBase.xml e quella nuova:

containerSpecial.xml: 
<beans> 
    <import resource="containerBase.xml" /> 

    <bean id="codebase" class="com.example.CodeBase"> 
     <property name="sourceCodeLocations"> 
      <list> 
       <value>src/handmade/productive</value> 
       <value>src/generated/productive</value> 
      </list> 
     </property> 
    </bean> 
</beans> 

C'è un modo per estendere la lista senza ridefinire il fagiolo?

EDIT 2009-10-06:

Lo scopo di questo è di avere un contenitore standard condiviso containerBase che viene utilizzato da un sacco di progetti diversi. Ogni progetto può sovrascrivere/estendere alcune proprietà che sono speciali per quel progetto nel proprio containerSpecial. Se il progetto non esegue l'override, utilizza le impostazioni predefinite definite in containerBase.

risposta

8

È possibile utilizzare un BeanFactoryPostProcessor per modificare i metadati del bean prima che il contenitore Spring instanzia il bean CodeBase. Per esempio:

public class CodebaseOverrider implements BeanFactoryPostProcessor { 

    private List<String> sourceCodeLocations; 

    public void postProcessBeanFactory(
      ConfigurableListableBeanFactory beanFactory) throws BeansException {   
     CodeBase codebase = (CodeBase)beanFactory.getBean("codebase"); 
     if (sourceCodeLocations != null) 
     { 
      codebase.setSourceCodeLocations(sourceCodeLocations); 
     } 
    } 

    public void setSourceCodeLocations(List<String> sourceCodeLocations) { 
     this.sourceCodeLocations = sourceCodeLocations; 
    } 

} 

Poi nel contextSpecial.xml:

<beans> 
    <import resource="context1.xml" /> 

    <bean class="com.example.CodebaseOverrider"> 
     <property name="sourceCodeLocations"> 
      <list> 
       <value>src/handmade/productive</value> 
       <value>src/generated/productive</value> 
      </list> 
     </property> 
    </bean> 
</beans> 
+0

CodebaseOverrider non è proprio quello che sto cercando, ma con questo approccio potrei scrivere facilmente un CodebaseListExtender. Lo proverò. – tangens

3

Sì. Una definizione di bean può avere un attributo "parent" che fa riferimento a una definizione di bean padre. La nuova definizione "figlio" eredita la maggior parte delle proprietà del genitore e qualsiasi di queste proprietà può essere sovrascritta.

Vedi Bean Definition Inheritance

Inoltre è possibile utilizzare Collection Merging di fondere la definizione di proprietà lista dalle definizioni padre e fagioli bambino. In questo modo è possibile specificare alcuni elementi dell'elenco nella definizione del bean padre e aggiungere altri elementi nella definizione del bean figlio.

+0

Ma poi ci sono due fagioli. "codebase" e quello in "containerSpecial.xml". Dall'applicazione voglio solo trovare un bean. E non voglio rendere il bean "codebase" in "containerBase.xml" astratto, perché è necessario da qualche altra parte. – tangens

+0

Non è necessario assegnare un nome a entrambi i bean "codebase" perché possono avere nomi univoci. Inoltre, non è necessario rendere astratto il bean parent che non è un requisito, ma solo un'opzione. – kdubb

+0

Tra l'altro è anche necessario cercare "raccolta-fusione". Risolve il problema di dover specificare nuovamente gli elementi di raccolta che sono già specificati nella definizione del bean padre. – kdubb

1

3 approcci:

  1. semplici: avere due liste defaultSourceCodeLocations e additionalSourceCodeLocations e avere i vostri metodi di accesso controllare entrambi questi (o combinare). Ho visto questo fatto in alcuni framework: viene compilato un elenco predefinito di gestori e aggiunti altri utenti creati ...

  2. Più complicato ma mantiene pulita la classe originale: è quindi possibile creare una classe CodeBaseModifier. Questo avrebbe un metodo init per modificare un'istanza iniettata del bean.

    <bean id="codebaseModifier" class="com.example.CodeBase" init-method="populateCodeBase"> 
        <property name="sourceCodeLocations" ref="codebase"/> 
        <property name="additionalSourceCodeLocations"> 
        <list> 
         <value>src/handmade/productive</value> 
        </list> 
        </property> 
    </bean> 
    

Se si voleva fare davvero questo generico si potrebbe fare un modificatore di fagioli che farebbe questo per riflessione. Stai attento all'ordine se usi questo approccio. I bean dipendenti da CodeBase dovrebbero assicurarsi che questa classe sia stata istanziata per prima (con dipende da)

3 Una variazione su 2 ... Invece di creare direttamente una classe CodeBase invece creare una factory che restituisce un bean popolato. Questa fabbrica potrebbe quindi essere configurata con Spring in modo simile a 2.Avere un defaultSourceCodeLocations e additionalSourceCodeLocations

A meno che non avete bisogno di un sacco di proprietà estensibili Vorrei andare con l'opzione 1.

+0

Non è possibile ottenere la formattazione corretta ... – Pablojim

1

C'è un modo per definire l'elenco in proprietà o altre configurazioni prima mano?

Sembra che la configurazione dell'app e il cablaggio siano strettamente accoppiati. Dalla mia esperienza, se è difficile fare qualcosa in primavera, probabilmente c'è un modo diverso per farlo.

+0

Sono libero di farlo nel modo migliore. Hai un'idea? – tangens

+0

I file di proprietà sono una semplice configurazione di sistema e possono essere iniettati in bean di primavera tramite gli oggetti Proprietà. Li ho usati per proprietà predefinite/container simili. Per ottenere il supporto dell'elenco, delegare un metodo per analizzare l'elenco dall'oggetto Proprietà. config.default.properties --------------------------- source.locations.1 = src/handmade/produttivo origine .locations.2 = src/handmade/produttivo1 config.container.properties --------------------------- origine. locations.1 = src/container/produttivi source.locations.2 = src/container/produttivi1 – jon077