web-dev-qa-db-ja.com

Spring MessageSourceは複数のクラスパスをサポートしていますか?

Springフレームワークを使用してWebベースのアプリケーション用のプラグインシステムを設計しています。プラグインはクラスパス上のjarファイルです。だから私はjspなどのソースを取得することができます、以下を参照してください

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

ここまでは順調ですね。しかし、messageSourceに問題があります。 ReloadableResourceBundleMessageSource#setBasenameNOT support "classpath *:"を介した複数のクラスパスを使用しているようです。 「クラスパス:」、messageSourceは1つのプラグインからのみ取得します。

すべてのプラグインからmessageSourcesを登録する方法を誰かが知っていますか? MessageSourceのそのような実装は存在しますか?

18
banterCZ

ここでの問題は、複数のクラスパスやクラスローダーではなく、特定のパスに対してコードが試行してロードするリソースの数にあります。

classpath*構文はSpringメカニズムであり、コードが特定のパスに対して複数のリソースをロードできるようにします。とても便利な。ただし、ResourceBundleMessageSourceは標準のJava.util.ResourceBundleを使用してリソースをロードします。これは、指定されたパスの最初のリソースをロードし、それ以外はすべて無視する、はるかに単純で扱いにくいメカニズムです。

私はあなたのために本当に簡単な修正を持っていません。 ResourceBundleMessageSourceを捨てて、MessageSourceを使用して検索するAbstractMessageSourceのカスタム実装を作成する必要があると思います(おそらくPathMatchingResourcePatternResolverをサブクラス化することによって)さまざまなリソースを公開し、MessageSourceインターフェースを介して公開します。 ResourceBundleはあまり役に立ちません。

8
skaffman

@ 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>
18
ajaristi

以下のようなことを行うことができます-基本的に、関連する各ベース名を明示的に指定します。

 <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>
9
Raghuram

別の方法として、以下の例のように、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>

これでうまくいくと思います。

2
seralex.vi

オーバーライドReloadableResourceBundleMessageSource::calculateFilenamesForLocale 多分よくなる。次に、ReloadableResourceBundleMessageSource::getPropertiesPropertiesHolderからcachedPropertiesを取得できます

2
Jia Feng

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アノテーションを配置するだけです。

0
jglatre