Guida / spiegazione della gerarchia di configurazione del file XML di Spring

Quando ho iniziato a conoscere Spring, le cose sono state configurate nel file applicationContext.xml. Poi, quando ho iniziato a leggere libri in particolare sulle versioni più recenti di Spring, hanno tutti eseguito la configurazione in file XML separati come myapp-servlet-xml, myapp-security.xml, myapp-service.xml, ecc., Di configurazione di contextConfigLocation nel file web.xml. Quindi, ad esempio, il codice che ho seguito ha avuto questo poiché è contextConfigLocation:

 contextConfigLocation  /WEB-INF/myapp-servlet.xml /WEB-INF/myapp-data.xml   

Comunque, recentemente mi sono imbattuto in un problema di configurazione (che le persone utili qui a StackOverflow mi hanno aiutato a capire) era dovuto a questa separazione. Non c’era alcun file applicationContext.xml per gli esempi da questi libri e più tardi quando ho provato ad aggiungere la scansione e le annotazioni automatiche all’app causando problemi. Ho provato a spostare tutto in applicationContext.xml e ad eliminare gli altri file e ho risolto il problema. Nient’altro è cambiato, ho solo messo tutto in applicationContext.xml.

Quindi, questo, insieme ai commenti degli altri, mi ha portato a capire che anche se non si crea un’applicazione applicationContext.xml, è ancora in uso ed è il livello più alto di una sorta di gerarchia di configurazione. Spero che qualcun altro possa spiegarmi come funziona tutto questo perché non ho trovato alcuna spiegazione su di esso da nessuna parte.

Ad esempio, se inserisco un determinato contesto: i tag di scansione dei componenti nei file di configurazione che si trovano sotto applicationContext.xml, potrebbe impedire l’esecuzione di determinate scansioni a determinate classi. Cose di questa natura. Non capisco la precedenza e cosa deve andare dove per essere sicuri che sia visto in tutta l’applicazione e così via. Se qualcuno può spiegarlo chiaramente o indicarmi una risorsa che lo spiega, lo apprezzerei molto, grazie. Spero che ciò che sto chiedendo abbia senso.

Non c’è nulla di speciale nel file “applicationContext.xml”, tranne che è il nome che Spring tende ad aspettarsi come file di configurazione predefinito. Utilizzando un file chiamato così o più file denominati “dog.xml”, “cat.xml” e “alien.xml” funzioneranno esattamente allo stesso modo. Il problema derivante dall’avere più ApplicationContexts in uso allo stesso tempo, non dall’avere più file XML. Di recente ho risposto a un paio di domande da persone che hanno avuto problemi a causa della mancata comprensione di questi concetti. Dai un’occhiata a queste risposte e vedi quali domande hai ancora:

Dichiarazione di Spring Bean nel contesto padre vs contesto figlio

Spring-MVC: cosa sono un “contesto” e uno “spazio dei nomi”?

Modifica: in risposta alla tua nuova domanda:

Ho avuto un nel mio servlet.xml.

Sto indovinando che questo file “servlet.xml” è chiamato come foo-servlet.xml , dove il DispatcherServlet configurato nel tuo web.xml è chiamato “foo”, come

  foo org.springframework.web.servlet.DispatcherServlet  

Per convenzione, all’avvio di questo DispatcherServlet, verrà creato un nuovo ApplicationContext configurato dal file foo-servlet.xml , derivato dal servlet-name . Ora, dato che hai inserito un context:component-scan , eseguirà la scansione in modo ricorsivo del pacchetto e creerai i bean per tutte le classi annotate. Il pacchetto che hai fornito, com.myapp , sembra che sia il pacchetto base per l’intera app, quindi Spring creerà i bean da tutte le classi annotate nella tua app, comprese quelle di accesso ai dati, in questo ApplicationContext associato al DispatcherServlet. In genere, questo contesto dovrebbe avere solo roba e bean di livello di visualizzazione che supportano direttamente DispatcherServlet, quindi si trattava di una configurazione errata.

Nel mio file data.xml avevo i bean di origine dati e basta. Nessun altro bean, tutto il resto è stato creato e annotato.

Presumibilmente, questo file “data.xml” è quello che hai elencato nel context-param contesto contextConfigLocation . Supponendo che tu abbia anche aggiunto ContextLoaderListener al tuo web.xml , come

  org.springframework.web.context.ContextLoaderListener  

allora quel file sarà usato per creare un secondo ApplicationContext – il contesto di root. Questo è ciò che fa questo ascoltatore. Nota che in realtà crea il contesto da tutti i file elencati in contextConfigLocation e, se hai incluso anche il tuo “servlet.xml” in quell’elenco, hai caricato due volte quella configurazione: qui nel contesto root e nel contesto associato a DipatcherServlet. Si spera che ora si veda la differenza tra i file di configurazione XML e ApplicationContexts che configurano. Lo stesso file XML può essere facilmente utilizzato per configurare due diversi contesti. Se farlo è corretto o meno è un’altra domanda. In questo caso particolare, non lo è.

L’ordine in cui ho descritto questi due contesti è in realtà indietro. Stavo solo seguendo la tua descrizione di quello che hai fatto. ContextLoaderListener, essendo un ServletContextListener , verrà sempre eseguito prima dell’avvio di qualsiasi servlet. Ciò significa che il contesto di root viene creato per primo e l’altro secondo per secondo. Questo è progettato in modo tale che quando DispatcherServlet crea il suo contesto, può aggiungere quel contesto come figlio del contesto di root. Ho descritto questa relazione in quegli altri post. L’effetto più importante di questo è che i bean nel contesto di root sono disponibili per e tramite il contesto di DispatcherServlet. Questo vale anche per le relazioni autowired. Questo è importante perché DispatcherServlet cerca nel suo contesto associato solo i bean di cui ha bisogno, come le istanze del controller. I controller, tuttavia, devono ovviamente essere cablati con bean di supporto. Pertanto, tradizionalmente, i controllori vivono nel contesto di DispatcherServlet e i bean di supporto vivono nel contesto di root.

Ho quindi provato ad aggiungere @Transacational al mio bean di servizio e non sarebbe persistito.

Affinché @Transactional funzioni, è necessario includere il nella configurazione di ApplicationContext in cui risiede il bean annotato. Il trucco sta nel capire la parte “dove vive”. I bean di un bambino possono sovrascrivere i bean in un contesto padre. Quindi – sto solo indovinando qui – se avessi caricato tutti i tuoi bean nel contesto DispatcherServlet come ho descritto sopra ma nel contesto root, potresti avere un bean nel contesto root questo è correttamente transazionale, ma non è quello che viene usato perché il duplicato è “più vicino” al servlet nella gerarchia padre / figlio, e il contesto in cui si trova non ha una configurazione .

Quando ho cambiato il contesto servlet: il tag component-scan invece punta a com.myapp.web e poi ha aggiunto un contesto: tag component-scan al file data.xml, tutto ha funzionato.

Dipende ancora in qualche modo da esattamente quali file di configurazione hai incluso in quali ApplicationContexts, ma per lo meno posso dire che, facendo ciò, hai rimosso molti bean dal contesto di DispatcherServlet che stavano causando problemi. In particolare, i bean @Transactional correttamente configurati nel contesto radice non sarebbero più ombreggiati dai bean nel contesto figlio e verrebbero iniettati nei controller, quindi la roba di persistenza funzionerebbe in quel momento.

Quindi … la cosa principale da togliere è che hai due ApplicationContex correlati. Devi rimanere consapevole di questo fatto e mantenere il controllo su quali fagioli andare in quale contesto.

Questo copre tutto?