2010-10-08 12 views
14

Sto progettando un sistema di plugin per la nostra applicazione basata sul web utilizzando il framework Spring. I plugin sono jar su classpath. Quindi sono in grado di ottenere fonti come jsp, vedi sottoSpring MessageSource supporta percorsi di classi multiple?

ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); 
Resource[] pages = resolver.getResources("classpath*:jsp/*jsp"); 

Fin qui tutto bene. Ma ho un problema con il messageSource. Mi sembra che ReloadableResourceBundleMessageSource#setBasename non sia NON supporti il ​​percorso di classe multiplo tramite "classpath *:" Se utilizzo solo "classpath:", ottengo il messaggio Origine solo da un unico plug-in.

Qualcuno ha un'idea di come registrare messageSources da tutti i plugin? Esiste una tale implementazione di MessageSource?

risposta

8

Il problema qui non è con più classpath o classloader, ma con quante risorse il codice cercherà di carico per un determinato percorso.

La sintassi classpath* è un meccanismo Spring, che consente al codice di caricare più risorse per un determinato percorso. Molto maneggevole. Tuttavia, ResourceBundleMessageSource utilizza lo standard java.util.ResourceBundle per caricare le risorse e questo è un meccanismo più semplice, più silenzioso, che caricherà la prima risorsa per un determinato percorso e ignorerà tutto il resto.

Non ho davvero una soluzione facile per te. Penso che dovrai abbandonare lo ResourceBundleMessageSource e scrivere un'implementazione personalizzata di MessageSource (molto probabilmente sottoclasse AbstractMessageSource) che utilizza PathMatchingResourcePatternResolver per individuare le varie risorse ed esporle tramite l'interfaccia MessageSource. ResourceBundle non sarà di grande aiuto.

+0

Grazie! È qualcosa di cui mi preoccupo. – banterCZ

+0

Per una soluzione che funziona guarda [risposta di ajaristi] (http://stackoverflow.com/a/27532814/606662) –

9

Si potrebbe fare qualcosa di simile a quanto segue: in sostanza, si specifica esplicitamente ciascun basename pertinente.

<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"> 
     <property name="basenames"> 
      <list> 
       <value>classpath:com/your/package/source1</value> 
       <value>classpath:com/your/second/package/source2</value> 
       <value>classpath:com/your/third/package/source3/value> 
       <value>classpath:com/your/fourth/package/source4</value> 
      </list> 
     </property> 
    </bean> 
+5

Sì, è vero. Ma devi conoscere tutti i plugin in anticipo. L'anima dovrebbe essere universale per i plugin. – banterCZ

+4

Mi hai appena insegnato come inserire i percorsi del pacchetto nei valori. –

2

In alternativa, si potrebbe sovrascrivere refreshProperties metodo da ReloadableResourceBundleMessageSource classe come qui di seguito Esempio:

public class MultipleMessageSource extends ReloadableResourceBundleMessageSource { 
    private static final String PROPERTIES_SUFFIX = ".properties"; 
    private PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); 

    @Override 
    protected PropertiesHolder refreshProperties(String filename, PropertiesHolder propHolder) { 
    Properties properties = new Properties(); 
    long lastModified = -1; 
    try { 
     Resource[] resources = resolver.getResources(filename + PROPERTIES_SUFFIX); 
     for (Resource resource : resources) { 
     String sourcePath = resource.getURI().toString().replace(PROPERTIES_SUFFIX, ""); 
     PropertiesHolder holder = super.refreshProperties(sourcePath, propHolder); 
     properties.putAll(holder.getProperties()); 
     if (lastModified < resource.lastModified()) 
      lastModified = resource.lastModified(); 
     } 
    } catch (IOException ignored) { } 
    return new PropertiesHolder(properties, lastModified); 
    } 
} 

e utilizzarlo con configurazione contesto primavera come ReloadableResourceBundleMessageSource:

<bean id="messageSource" class="common.utils.MultipleMessageSource"> 
    <property name="basenames"> 
     <list> 
     <value>classpath:/messages/validation</value> 
     <value>classpath:/messages/messages</value> 
     </list> 
    </property> 
    <property name="fileEncodings" value="UTF-8"/> 
    <property name="defaultEncoding" value="UTF-8"/> 
    </bean> 

Penso che questo dovrebbe fare il trucco .

10

Con la soluzione di @ seralex-vi nome/WEB-INF/i messaggi non funzionavano.

ho sovrascritto le refreshProperties metodo sul ReloadableResourceBundleMessageSource classe wich eseguire entrambi i tipi di BaseNames (classpath *: e/WEB-INF /)

public class SmReloadableResourceBundleMessageSource extends ReloadableResourceBundleMessageSource { 

private static final String PROPERTIES_SUFFIX = ".properties"; 

private PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); 

@Override 
protected PropertiesHolder refreshProperties(String filename, PropertiesHolder propHolder) { 
    if (filename.startsWith(PathMatchingResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX)) { 
     return refreshClassPathProperties(filename, propHolder); 
    } else { 
     return super.refreshProperties(filename, propHolder); 
    } 
} 

private PropertiesHolder refreshClassPathProperties(String filename, PropertiesHolder propHolder) { 
    Properties properties = new Properties(); 
    long lastModified = -1; 
    try { 
     Resource[] resources = resolver.getResources(filename + PROPERTIES_SUFFIX); 
     for (Resource resource : resources) { 
     String sourcePath = resource.getURI().toString().replace(PROPERTIES_SUFFIX, ""); 
     PropertiesHolder holder = super.refreshProperties(sourcePath, propHolder); 
     properties.putAll(holder.getProperties()); 
     if (lastModified < resource.lastModified()) 
      lastModified = resource.lastModified(); 
     } 
    } catch (IOException ignored) { 
    } 
    return new PropertiesHolder(properties, lastModified); 
} 

Sulla primavera-context.xml è necessario avere la classpath *: prefisso

<bean id="messageSource" class="SmReloadableResourceBundleMessageSource"> 
    <property name="basenames"> 
     <list> 
      <value>/WEB-INF/i18n/enums</value> 
      <value>/WEB-INF/i18n/messages</value> 
      <value>classpath*:/META-INF/messages-common</value> 
      <value>classpath*:/META-INF/enums</value> 
     </list> 
    </property> 
</bean> 
+5

Questa dovrebbe essere la risposta, dà una soluzione e funziona. Grazie – Don

0

È possibile usufruire di configurazione di Java e fonti di messaggi gerarchiche per costruire un sistema di plugin molto semplice. In ogni vasetto pluggable cadere una classe come questa:

@Configuration 
public class MyPluginConfig { 
    @Bean 
    @Qualifier("external") 
    public HierarchicalMessageSource mypluginMessageSource() { 
     ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); 
     messageSource.setBasenames("classpath:my-plugin-messages"); 
     return messageSource; 
    } 
} 

ei corrispondenti my-plugin-messages.properties file.

Nella classe di configurazione dell'applicazione Java principale messo qualcosa di simile:

@Configuration 
public class MainConfig { 
    @Autowired(required = false) 
    @Qualifier("external") 
    private List<HierarchicalMessageSource> externalMessageSources = Collections.emptyList(); 

    @Bean 
    public MessageSource messageSource() { 
     ReloadableResourceBundleMessageSource rootMessageSource = new ReloadableResourceBundleMessageSource(); 
     rootMessageSource.setBasenames("classpath:messages"); 

     if (externalMessageSources.isEmpty()) { 
      // No external message sources found, just main message source will be used 
      return rootMessageSource; 
     } 
     else { 
      // Wiring detected external message sources, putting main message source as "last resort" 
      int count = externalMessageSources.size(); 

      for (int i = 0; i < count; i++) { 
       HierarchicalMessageSource current = externalMessageSources.get(i); 
       current.setParentMessageSource(i == count - 1 ? rootMessageSource : externalMessageSources.get(i + 1)); 
      } 
      return externalMessageSources.get(0); 
     } 
    } 
} 

Se l'ordine di plugin è rilevante, basta mettere @Order annotazioni in ogni chicco sorgente del messaggio pluggable.

Problemi correlati