Spring MessageSource是否支持多类路径?

我正在使用Spring框架为基于Web的应用程序设计插件系统。 插件是类路径上的jar。 所以我能够得到像jsp这样的资源,见下文

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

到现在为止还挺好。 但我的messageSource有问题。 在我看来, ReloadableResourceBundleMessageSource #setBasename不支持通过“classpath *:”的多个类路径。如果我只使用“classpath:”,我只从一个插件获取messageSource。

有没有人知道如何从所有插件注册messageSources? 是否存在MessageSource的这种实现?

这里的问题不是多个类路径或类加载器,而是代码将尝试为给定路径加载多少资源。

classpath*语法是一种Spring机制,允许代码为给定路径加载多个资源。 非常便利。 但是, ResourceBundleMessageSource使用标准的java.util.ResourceBundle来加载资源,这是一个更简单的dumber机制,它将加载给定路径的第一个资源,并忽略其他所有内容。

我真的没有一个简单的解决方案。 我认为你将不得不抛弃ResourceBundleMessageSource并编写MessageSource的自定义实现(很可能通过子类化AbstractMessageSource ),它使用PathMatchingResourcePatternResolver来定位各种资源并通过MessageSource接口公开它们。 ResourceBundle不会有太大帮助。

随着@ seralex-vi basenames / WEB-INF / messages的解决方案无效。

我在类ReloadableResourceBundleMessageSource上覆盖方法refreshProperties,它执行两种类型的基本名称(classpath *:和/ 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); } 

在spring-context.xml上,您必须具有classpath *:前缀

    /WEB-INF/i18n/enums /WEB-INF/i18n/messages classpath*:/META-INF/messages-common classpath*:/META-INF/enums    

您可以执行类似于下面的操作 – 实质上明确指定每个相关的基本名称。

     classpath:com/your/package/source1 classpath:com/your/second/package/source2 classpath:com/your/third/package/source3/value> classpath:com/your/fourth/package/source4    

或者,您可以从refreshProperties类重写refreshProperties方法,如下例所示:

 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

     classpath:/messages/validation classpath:/messages/messages      

我认为这应该可以解决问题。

您可以利用Java配置和分层消息源来构建一个非常简单的插件系统。 在每个可插入的jar中放一个这样的类:

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

如果插件的顺序是相关的,只需在每个可插入消息源bean中放入@Order注释即可。

重写ReloadableResourceBundleMessageSource::calculateFilenamesForLocale可能会更好。 然后, ReloadableResourceBundleMessageSource::getProperties可以从cachedProperties获取PropertiesHolder