Avvio personalizzato di avvio a molla: come si contribuiscono i messaggi i18n a MessageSource?

Sto scrivendo un avvio Spring Boot personalizzato che altri sviluppatori inseriranno nelle loro applicazioni e questo antipasto contiene controller e schermate dell’interfaccia utente pronti all’uso.

Queste schermate dell’interfaccia utente sono internazionalizzate e le chiavi / valori i18n si trovano in un file di pacchetto: com/foo/wherever/i18n.properties .

Voglio assicurarmi che quando il mio dispositivo d’avviamento è caricato all’avvio, questi i18n.properties siano disponibili automaticamente in MessageSource dell’applicazione in modo che le mie pagine UI funzionino (renderizzate tramite normali controller Spring + ViewResolver + Visualizza implementazioni) senza che lo sviluppatore dell’app debba specificare questo file stessi .

In altre parole, dovrebbero essere in grado di aggiungere il mio dispositivo d’avviamento al loro percorso di class di runtime e tutto ‘funziona’ senza la necessità di configurare nulla.

Ora, ho scoperto che lo sviluppatore dell’app può creare il proprio file src/main/resources/messages.properties e configurare manualmente il file dei messaggi aggiuntivi in application.properties :

 spring.messages.basename = messages, com.foo.wherever.i18n 

E questo funzionerà.

Tuttavia, ciò richiede quanto segue:

  1. Devono configurare manualmente la proprietà spring.messages.basename – non è automatico. e
  2. Devono avere il proprio file messages.properties nel percorso di class dell’applicazione. Se un file messages.properties non esiste, spring.messages.basename non funziona nemmeno. Anche se non si preoccupano di i18n, questo è ancora richiesto, non desiderabile.

Suppongo che potrei spostare il mio file i18n.properties in un percorso classpath: /messages.properties nello starter .jar, ma questa non sembra una buona soluzione: se l’app dev ha il proprio file messages.properties solo uno di sarebbero letti, risultando in valori di messaggio mancanti.

Sembra che Spring Boot MessageSourceAutoConfiguration abbia un concetto di CompositeMessageSource che itera su una o più istanze di MessageSource che sono disponibili (e Ordered) nel Spring ApplicationContext e che viene utilizzato da DispatcherServlet. Ciò consentirebbe a qualsiasi antipasto di contribuire ai messaggi disponibili semplicemente dichiarando un MessageSource nel proprio auto config

È ansible fare ciò che chiedo? Qual è la soluzione più “hands off” per lo sviluppatore dell’app?

Forse è lungo, ma puoi provare a usare BeanFactoryPostProcessor .

L’idea è la seguente:

  1. Prendi il bean “messageSource” fuori dal contesto dell’applicazione. Si noti che potrebbe essere, ma non deve essere uno di avvio di spring se ad esempio lo sviluppatore vuole utilizzare la propria implementazione e non utilizzare la configurazione automatica di avvio di spring.

  2. Sostituiscilo con la tua implementazione che tenta di risolvere “le tue chiavi” e il resto delegato all’origine del messaggio originale. O viceversa se vuoi rendere ansible l’override delle traduzioni da parte dello sviluppatore (ci possono essere problemi se l’origine del messaggio originale non genera eccezioni per le chiavi sconosciute).

Ma potrebbe esserci un modo migliore per farlo.

L’ho impostato nel modo seguente. Attualmente sto solo supportando en_US ma è configurato per gestire un numero qualsiasi di lingue usando l’internazionalizzazione (i18n).

Visualizza il codice qui

Visualizza il codice di sintesi qui: Codice su github gist

Aggiungi origine messaggio e Fagioli locali predefiniti

Aggiungi questi bean a Application.java per impostare le impostazioni locali predefinite e configurare la posizione dei puntelli dei messaggi

Origine messaggi e impostazioni locali predefinite


Crea un servizio messaggi

Il servizio otterrà le impostazioni locali predefinite dalla sessione e quindi otterrà il testo del messaggio dai propri oggetti di scena

Messaggio di installazione svc


Utilizzare il servizio messaggi in Controller

Iniettare il messaggio svc e quindi passare l’id per ottenere il valore dal file props

Usa svc nel controller


Aggiungi il file message.properties nella locale

Goto / risorse:

  • crea la cartella locale
  • crea un file chiamato messages_en_US.properties

puntelli del messaggio


Leggi di più

È ansible visualizzare un articolo più completo su questo argomento qui: Spring Boot Internationalization i18n utilizzando Message Properties


Guarda il codice

Visualizza il codice di sintesi qui: Codice su github gist

Mi rendo conto che questa è una domanda vecchia e risposta ormai, ma ho incontrato lo stesso problema l’altro giorno e ho scritto un post sul blog su come ho deciso di risolverlo. Ho pensato di condividerlo qui da quando ho tratto ispirazione per la mia soluzione da questo thread.

In breve, ci vuole l’idea di sodik di intercettare la creazione del bean MessageSource , ma invece di usare un BeanFactoryPostProcessor sto usando un BeanPostProcessor , e invece di sostituire l’originale MessageSource nel contesto dell’applicazione, aggiungo solo il mio come genitore:

 @Bean BeanPostProcessor messageSourceCustomExtender() { return new BeanPostProcessor() { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof HierarchicalMessageSource && beanName.equals("messageSource")) { ResourceBundleMessageSource parent = new ResourceBundleMessageSource(); parent.setBasename("custom"); ((HierarchicalMessageSource) bean).setParentMessageSource(parent); } return bean; } }; } 

Puoi leggere il post completo del blog in cui spiego alcuni avvertimenti sulla mia soluzione: http://www.thomaskasene.com/2016/08/20/custom-spring-boot-starter-messagesource/

Aggiornare

Dopo alcuni tentativi, mi sono reso conto che l’utilizzo di un BeanFactoryPostProcessor era errato perché causerebbe la creazione prematura del bean MessageSource originale e ignora le proprietà dell’applicazione (soprattutto, spring.messages.basename ). Ciò significa che l’applicazione non sarà in grado di configurare queste proprietà. Vedere l’estratto dalla documentazione di BeanFactoryPostProcessor di seguito.

Un BeanFactoryPostProcessor può interagire con e modificare le definizioni di bean, ma mai istanze bean. Ciò potrebbe causare un’anticipazione dei fagioli prematura, violando il contenitore e causando effetti collaterali indesiderati. Se è richiesta l’interazione dell’istanza di bean, prendere in considerazione l’implementazione di BeanPostProcessor.

Ho aggiornato l’esempio precedente per utilizzare invece BeanPostProcessor , che altera l’istanza del bean piuttosto che la definizione del bean.