Springフレームワークを使用してWebベースのアプリケーション用のプラグインシステムを設計しています。プラグインはクラスパス上のjarファイルです。だから私はjspなどのソースを取得することができます、以下を参照してください
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] pages = resolver.getResources("classpath*:jsp/*jsp");
ここまでは順調ですね。しかし、messageSourceに問題があります。 ReloadableResourceBundleMessageSource#setBasename はNOT support "classpath *:"を介した複数のクラスパスを使用しているようです。 「クラスパス:」、messageSourceは1つのプラグインからのみ取得します。
すべてのプラグインからmessageSourcesを登録する方法を誰かが知っていますか? MessageSourceのそのような実装は存在しますか?
ここでの問題は、複数のクラスパスやクラスローダーではなく、特定のパスに対してコードが試行してロードするリソースの数にあります。
classpath*
構文はSpringメカニズムであり、コードが特定のパスに対して複数のリソースをロードできるようにします。とても便利な。ただし、ResourceBundleMessageSource
は標準のJava.util.ResourceBundle
を使用してリソースをロードします。これは、指定されたパスの最初のリソースをロードし、それ以外はすべて無視する、はるかに単純で扱いにくいメカニズムです。
私はあなたのために本当に簡単な修正を持っていません。 ResourceBundleMessageSource
を捨てて、MessageSource
を使用して検索するAbstractMessageSource
のカスタム実装を作成する必要があると思います(おそらくPathMatchingResourcePatternResolver
をサブクラス化することによって)さまざまなリソースを公開し、MessageSource
インターフェースを介して公開します。 ResourceBundle
はあまり役に立ちません。
@ seralex-vi basenames/WEB-INF/messagesのソリューションでは、機能しませんでした。
両方のタイプのベース名(クラスパス*:および/ WEB-INF /)を実行するクラスReloadableResourceBundleMessageSourceのメソッドrefreshPropertiesを上書きしました
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 *:プレフィックスが必要です。
<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>
以下のようなことを行うことができます-基本的に、関連する各ベース名を明示的に指定します。
<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>
別の方法として、以下の例のように、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);
}
}
ReloadableResourceBundleMessageSource
のようなSpringコンテキスト構成で使用します。
<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>
これでうまくいくと思います。
オーバーライドReloadableResourceBundleMessageSource::calculateFilenamesForLocale
多分よくなる。次に、ReloadableResourceBundleMessageSource::getProperties
PropertiesHolder
からcachedProperties
を取得できます
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 configクラスは次のようになります。
@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);
}
}
}
プラグインの順序が適切な場合は、プラグイン可能な各メッセージソースBeanに@Order
アノテーションを配置するだけです。