web-dev-qa-db-ja.com

Spring Frameworkの注釈を介してresourceBundleからローカライズされたメッセージを取得する

これは可能ですか?現在それはこのように行われます:

<bean id="resource" class="org.springframework.context.support.ResourceBundleMessageSource">
    <property name="basenames">
        <list>
            <value>content.Language</value> 
        </list>
    </property>
</bean>

@Autowired
protected MessageSource resource;

protected String getMessage(String code, Object[] object, Locale locale) {
    return resource.getMessage(code, object, locale);
}

@Valueアノテーションを介してプロパティを取得するような方法はありますか?

<util:properties id="generals" location="classpath:portlet.properties" />

    @Value("#{generals['supported.lang.codes']}")
    public String langCodes;

通常はメソッドを呼び出さなければならないので問題ありませんが、たとえば単体テストの場合、これは苦痛です... ...場合によっては、オブジェクトに初期化がないWebdriverのPageObjectパターンがあり、これは非常に役立ちます

15
lisak

重要なのは、これは単体テストでのみ本当に役立つということです。実際のアプリケーションでは、ロケールは、アノテーションにハードコードできないランタイム情報です。ロケールは、ランタイムのユーザーロケールに基づいて決定されます。

ところで、次のようなものを自分で簡単に実装できます:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface Localize {

    String value();

}

そして

public class CustomAnnotationBeanPostProcessor implements BeanPostProcessor {

    public Object postProcessAfterInitialization(Object bean, String beanName) {
        return bean;
    }

    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        Class clazz = bean.getClass();
        do {
            for (Field field : clazz.getDeclaredFields()) {
                if (field.isAnnotationPresent(Localize.class)) {
                    // get message from ResourceBundle and populate the field with it
                }
            }
            clazz = clazz.getSuperclass();
        } while (clazz != null);
        return bean;
    }
4
lisak

私はあなたが2つの概念を混合したと信じています:

  • プロパティファイル
  • メッセージリソースバンドル

プロパティファイルには、プロパティ(ロケールに依存しない)が含まれています。 Springでは、それらは、たとえばutil:propertiesを介してロードでき、@Valueアノテーションで使用できます。

ただし、メッセージリソースバンドル(プロパティファイルのように見えるファイルに基づく)は言語に依存します。 Springでは、org.springframework.context.support.ResourceBundleMessageSourceを介してそれらをロードできます。ただし、@Valueを介して文字列を挿入しないでください。 @ValueのインジェクションはBeanごとに1回行われ、@Valueは1回(ほとんどの場合は開始時に)評価され、計算された値がインジェクトされるため、それらをインジェクトすることはできません。しかし、これはメッセージリソースバンドルを使用するときに通常必要なものではありません。ユーザーの言語に応じて、変数が使用されるたびに値を評価する必要があるためです。


しかし、自分で簡単に作成できます!

必要なのはこのクラスだけです。

import Java.util.Locale;    
import javax.annotation.Resource;    
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;

@Configurable
public class MSG {

    private String key;

    @Resource(name = "messageSource")
    private MessageSource messageSource;

    public MSG(String key) {
        super();
        this.key = key;        
    }

    public String value() {
        Locale locale = LocaleContextHolder.getLocale();                        
        return messageSource.getMessage(key, new Object[0], locale);
    }

    @Override
    public String toString() {
        return value();
    }
}

その後、次のように使用できます。

@Service
public class Demo {

    @Value("demo.output.hallo")
    private MSG hallo;

    @Value("demo.output.world")
    private MSG world;

    public void demo(){
        System.out.println("demo: " + hello + " " + world);
    }    
}

これを実行するには、<context:spring-configured />を有効にしてAspectJ @Configurableサポートをオンにする必要があります。また、(それが重要です)Ressouce Bundle Message Sourceを同じアプリケーションコンテキストでインスタンス化する必要があります(たとえば、WebアプリケーションでReloadableResourceBundleMessageSource定義はほとんどの場合Webアプリコンテキストで使用されますが、MSGオブジェクトは「通常の」アプリケーションコンテキストにあるため、この場合は機能しません。

28
Ralph