2010-10-08 65 views
14

我正在爲使用Spring框架的基於Web的應用程序設計一個插件系統。插件是classpath上的jar。所以我能夠得到像jsp這樣的源代碼,看下面Spring MessageSource支持多類路徑嗎?

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

到目前爲止好。但是我對messageSource有問題。在我看來,ReloadableResourceBundleMessageSource#setBasename確實是不支持通過「classpath *:」的多個類路徑如果我只使用「classpath:」,我只從一個插件獲取messageSource。

有沒有人有一個想法如何註冊來自所有插件的messageSources?是否存在MessageSource的這種實現?

回答

8

這裏的問題是不是與多個類路徑或類加載器,但代碼將有多少資源,嘗試和負載對於給定的路徑。

classpath*語法是一種Spring機制,它允許代碼爲給定路徑加載多個資源。非常便利。然而,ResourceBundleMessageSource使用標準的java.util.ResourceBundle來加載資源,這是一個簡單得多的dumber機制,它將加載給定路徑的第一個資源,並忽略其他所有內容。

我真的沒有一個簡單的解決方案給你。我認爲你將不得不消除ResourceBundleMessageSource並編寫一個MessageSource(很可能通過繼承AbstractMessageSource)的定製實現,它使用PathMatchingResourcePatternResolver來定位各種資源並通過MessageSource接口公開它們。 ResourceBundle不會有太大的幫助。

+0

謝謝!這是我擔心的事情。 – banterCZ 2010-10-08 09:34:43

+0

對於解決方案,看看[ajaristi的答案](http://stackoverflow.com/a/27532814/606662) – 2016-06-07 10:02:31

9

你可以做類似於下面的事情 - 基本明確地指定每個相關的基名。

<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

是的,沒錯。但是你必須事先知道所有的插件。插件應該是通用的。 – banterCZ 2010-10-08 09:17:56

+4

您剛剛教會了我如何在值中輸入包路徑。 – 2011-12-11 01:07:11

2

作爲替代方案,您可以覆蓋refreshProperties方法從ReloadableResourceBundleMessageSource類像下面的例子:

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

,並與Spring上下文配置像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> 

我想這應該做的伎倆。

10

由於@ seralex-vi basenames/WEB-INF /消息的解決方案無法正常工作。

我overwrited的類ReloadableResourceBundleMessageSource方法refreshProperties至極執行這兩種類型的基本名稱的(類路徑*:和/ 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); 
} 

在彈簧的context.xml您必須類路徑*:前綴

<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

這應該是答案,它提供了一個解決方案,它的工作原理。謝謝 – Don 2015-11-06 04:14:45

0

你可以充分利用Java配置和分層消息源建立一個非常簡單的插件系統。在每個可插拔瓶子中放置這樣一個類:

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

和相應的my-plugin-messages.properties文件。

在主應用程序的Java配置類把這樣的事情:

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

如果插件的順序是相關的,只是把@Order標註在每個可插拔的消息源豆。