2010-10-08 9 views
14

Estoy diseñando un sistema de complemento para nuestra aplicación basada en web utilizando Spring framework. Los complementos son jar en classpath. Así que puedo obtener fuentes como jsp, consulte a continuación¿Spring MessageSource admite ruta de clase múltiple?

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

Hasta ahora todo bien. Pero tengo un problema con messageSource. Me parece que ReloadableResourceBundleMessageSource#setBasename hace NO es compatible con ruta de clase múltiple a través de "classpath *:" Si uso solo "classpath:", obtengo el messageSource solo desde un plugin.

¿Alguien tiene una idea de cómo registrar messageSources de todos los complementos? ¿Existe tal implementación de MessageSource?

Respuesta

8

La cuestión aquí no es con múltiples rutas de clases o cargadores de clases, pero con la cantidad de recursos del código tratará de carga para una ruta dada.

La sintaxis classpath* es un mecanismo de resorte, que permite que el código cargue múltiples recursos para una ruta determinada. Muy útil. Sin embargo, ResourceBundleMessageSource utiliza el estándar java.util.ResourceBundle para cargar los recursos, y este es un mecanismo mucho más simple, más pesado, que cargará el primer recurso para una ruta determinada e ignorará todo lo demás.

Realmente no tengo una solución fácil para usted. Creo que vas a tener que deshacerse de ResourceBundleMessageSource y escribir una implementación personalizada de MessageSource (lo más probable subclasificando AbstractMessageSource), que utiliza PathMatchingResourcePatternResolver para localizar los diversos recursos y exponerlos a través de la interfaz de MessageSource. ResourceBundle no va a ser de mucha ayuda.

+0

Gracias! Es algo que me preocupaba. – banterCZ

+0

Para una solución que funciona mira [respuesta de ajaristi] (http://stackoverflow.com/a/27532814/606662) –

9

Podría hacer algo similar a lo siguiente: esencialmente especifique cada uno de los nombres de pila relevantes explícitamente.

<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í, así es. Pero debes conocer todos los complementos de antemano. La soulution debería ser universal para plugins. – banterCZ

+4

Acabas de enseñarme cómo ingresar rutas de paquetes en valores. –

2

Como alternativa, se podría anular refreshProperties método de ReloadableResourceBundleMessageSource clase como ejemplo abajo:

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

y utilizarlo con la configuración contexto primavera como 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> 

creo que esto debería hacer el truco .

10

Con la solución de @ seralex-vi basenames/WEB-INF/mensajes no funcionó.

I sobreescrito los refreshProperties método en la clase ReloadableResourceBundleMessageSource Wich realizar ambos tipos de nombres base (ruta de clases *: y/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); 
} 

En la primavera-context.xml debe tener la ruta de clase *: prefijo

<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

Esta debería ser la respuesta, da una solución y funciona. Gracias – Don

0

Usted puede tomar ventaja de la configuración de Java y fuentes de mensajes jerárquicos para construir un sistema de plugins bastante simple. En cada frasco enchufable caer una clase como esta:

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

y los correspondientes archivos my-plugin-messages.properties.

En la clase principal de la aplicación Java config poner algo como esto:

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

Si el orden de los plugins es relevante, sólo hay que poner @Order anotaciones en cada grano de origen del mensaje enchufable.

Cuestiones relacionadas