2010-10-08 12 views
14

Ich entwerfe ein Plugin-System für unsere webbasierte Anwendung mit Spring Framework. Plugins sind Gläser im Klassenpfad. So kann ich Quellen wie Jsp erhalten, siehe untenUnterstützt Spring MessageSource mehrere Klassenpfad?

So weit so gut. Aber ich habe ein Problem mit der MessageSource. Es scheint mir, dass ReloadableResourceBundleMessageSource#setBasename tut nicht unterstützt mehrere Klassenpfad über die "Klassenpfad *:" Wenn ich nur "Classpath:" verwende, bekomme ich die MessageSource nur nur von einem Plugin.

Hat jemand eine Idee, wie man MessageSources von allen Plugins registriert? Existiert eine solche Implementierung von MessageSource?

Antwort

8

Das Problem hier ist nicht mit mehreren CLASSPATHs oder Classloader, aber mit, wie viele Ressourcen der Code wird versuchen und Last für einen bestimmten Pfad. Die classpath* Syntax ist ein Spring-Mechanismus, mit dem Code mehrere Ressourcen für einen bestimmten Pfad laden kann. Sehr praktisch. ResourceBundleMessageSource verwendet jedoch den Standard java.util.ResourceBundle, um die Ressourcen zu laden, und dies ist ein viel einfacherer Dümmer-Mechanismus, der die erste Ressource für einen bestimmten Pfad lädt und alles andere ignoriert.

Ich habe nicht wirklich eine einfache Lösung für Sie. Ich denke, Sie müssen ResourceBundleMessageSource Graben und schreiben Sie eine benutzerdefinierte Implementierung von MessageSource (am wahrscheinlichsten durch Unterklassen AbstractMessageSource), die PathMatchingResourcePatternResolver verwendet, um die verschiedenen Ressourcen zu finden und sie über die MessageSource Schnittstelle freizulegen. ResourceBundle wird nicht viel helfen.

+0

Danke! Ich habe mir Sorgen gemacht. – banterCZ

+0

Für eine Lösung, die funktioniert schauen Sie [Ajaristi Antwort] (http://stackoverflow.com/a/27532814/606662) –

9

Sie könnten etwas ähnliches wie unten tun - im Wesentlichen jeden relevanten Basisnamen explizit angeben.

<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

Ja, das stimmt. Aber du musst vorher alle Plugins kennen. Die Soulution sollte universell für Plugins sein. – banterCZ

+4

Sie haben mir gerade beigebracht, wie man Paketpfade in Werten einträgt. –

2

Als Alternative Sie refreshProperties Methode von ReloadableResourceBundleMessageSource Klasse wie unter Beispiel außer Kraft setzen könnte:

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

und verwenden Sie es mit Federkontextkonfiguration wie 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> 

ich denke, das den Trick tun sollten .

10

Mit der Lösung von @ seralex-vi Basisnamen/WEB-INF/Nachrichten hat nicht funktioniert.

ich die Methode refreshProperties auf die Klasse ReloadableResourceBundleMessageSource überschrieben wich beide Arten von Basisnamen führen (Classpath *: und/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); 
} 

Auf der feder context.xml müssen Sie die Classpath *: Präfix

<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

Dies sollte die Antwort sein, es gibt eine Lösung und es funktioniert. Vielen Dank – Don

0

Sie können die Vorteile von Java-Konfiguration und hierarchischen Nachrichtenquellen finden ganz einfaches Plugin-System zu bauen. In jedem steckbare jar eine Klasse wie folgt löschen:

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

und die entsprechenden my-plugin-messages.properties Dateien.

In der Hauptanwendung Java Config-Klasse setzen etwas wie folgt aus:

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

Wenn die Reihenfolge der Plugins relevant ist, stellen nur @Order Anmerkungen in jeder steckbaren Nachrichtenquelle Bohne.

Verwandte Themen