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