3

これは多くのフォーラムで問われているテーマですが、正確で具体的な答えは見つかりません。受け入れられた答えでさえ私の観点では不完全なので、この問題についての正確な質問と答えを構築することを期待して、この問題を解決するための私の完全な試みを投稿しようと思います。

JSFでリソースバンドルを機能させようとしています。リソースバンドルはSpringBeanから取得され、任意の外部システム(つまりデータベース)からロードする必要があります。

今のところデータベースクエリをバイパスし、モックアップされたリソースバンドルを使用して物事を明確にします。

これは私のリソースバンドルビジネスの実装であり、このフォーラムの他の投稿から収集することができました。

public class TesteResBundle extends ReloadableResourceBundleMessageSource {

    private final Map<String, Map<String, String>> properties = new HashMap<String, Map<String, String>>();

    public TesteResBundle() {
        reload();
    }

    @Override
    protected MessageFormat resolveCode(String code, Locale locale) {
        String msg = getText(code, locale);
        MessageFormat result = createMessageFormat(msg, locale);
        return result;
    }

    @Override
    protected String resolveCodeWithoutArguments(String code, Locale locale) {
        return getText(code, locale);
    }

    private String getText(String code, Locale locale) {
        Map<String, String> localized = properties.get(code);
        String textForCurrentLanguage = null;
        if (localized != null) {
            textForCurrentLanguage = localized.get(locale.getLanguage());
            if (textForCurrentLanguage == null) {
                textForCurrentLanguage = localized.get(Locale.ENGLISH.getLanguage());
            }
        }
        return textForCurrentLanguage != null ? textForCurrentLanguage : code;
    }

    public void reload() {
        properties.clear();
        properties.putAll(loadTexts());
    }

    protected Map<String, Map<String, String>> loadTexts() {

        Map<String, Map<String, String>> m = new HashMap<String, Map<String, String>>();
        Map<String, String> v = new HashMap<String, String>();
        v.put("en", "good");
        v.put("pt", "bom");
        v.put("en_US", "bom");
        m.put("prop", v);

        v = new HashMap<String, String>();
        v.put("en", "bad");
        v.put("pt", "mau");
        v.put("en_US", "bom");
        m.put("pror", v);
        return m;
    }
}

これは、私もフォーラムで入手したカスタムELリゾルバーです。ベースがMessageSourceのインスタンスである場合、メッセージを収集しようとします。そうでない場合は、解像度をデフォルトのSpringELリゾルバーに渡します。

public class MessageSourcePropertyResolver extends SpringBeanFacesELResolver {

    public Object getValue(ELContext elContext, Object base, Object property)
        throws ELException {

        if (base instanceof MessageSource && property instanceof String) {
            String result = ((MessageSource) base).getMessage(
                (String) property, null, getLocale());

            if (null != result) {
                elContext.setPropertyResolved(true);
            }

            return result;
        }

        return super.getValue(elContext, base, property);
    }

     private Locale getLocale() {
        FacesContext context = FacesContext.getCurrentInstance();
        return context.getExternalContext().getRequestLocale();
     }

}

カスタムELリゾルバーはfaces-config.xmlで定義されています。

<el-resolver>pt.teste.pojo.MessageSourcePropertyResolver</el-resolver>

最後に、Spring構成では、messageSourceBeanを次のように定義しています。

<bean id="messageSource" class="pt.teste.pojo.TesteResBundle">
</bean>

messageSource Beanが正しくインスタンス化され、アプリケーションの起動時にHashMapが正しくロードされていることを確認できます。カスタムハンドラーが呼び出され、リソースメッセージではなく正しく解決されているすべてのELがデフォルトのSpringリゾルバーに渡されていることを確認できます。

xhtml JSF 2.0ページでリソースバンドルを使用する場合、次のように実行します。

<h:outputText value="#{messageSource.prop}" />

EL解決中に、カスタムリゾルバーはベースをMessageSourceインスタンスとして正しく検出しますが、次の場合に失敗します。

String result = ((MessageSource) base).getMessage((String) property, null, getLocale());

次の例外を除いて:

org.springframework.context.NoSuchMessageException: No message found under code 'prop' for locale 'en_US'.
org.springframework.context.support.DelegatingMessageSource.getMessage(DelegatingMessageSource.java:65)
pt.teste.pojo.MessageSourcePropertyResolver.getValue(MessageSourcePropertyResolver.java:18)
com.sun.faces.el.DemuxCompositeELResolver._getValue(DemuxCompositeELResolver.java:176)
com.sun.faces.el.DemuxCompositeELResolver.getValue(DemuxCompositeELResolver.java:203)
org.apache.el.parser.AstValue.getValue(AstValue.java:169)
org.apache.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:189)
com.sun.faces.facelets.el.TagValueExpression.getValue(TagValueExpression.java:109)
javax.faces.component.ComponentStateHelper.eval(ComponentStateHelper.java:194)
javax.faces.component.ComponentStateHelper.eval(ComponentStateHelper.java:182)
javax.faces.component.UIOutput.getValue(UIOutput.java:169)
com.sun.faces.renderkit.html_basic.HtmlBasicInputRenderer.getValue(HtmlBasicInputRenderer.java:205)
com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.getCurrentValue(HtmlBasicRenderer.java:355)
com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeEnd(HtmlBasicRenderer.java:164)
javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:875)
com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeRecursive(HtmlBasicRenderer.java:312)
com.sun.faces.renderkit.html_basic.GroupRenderer.encodeChildren(GroupRenderer.java:105)
javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:845)
javax.faces.component.UIComponent.encodeAll(UIComponent.java:1779)
com.sun.faces.renderkit.html_basic.CompositeRenderer.encodeChildren(CompositeRenderer.java:78)
javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:845)
javax.faces.component.UIComponent.encodeAll(UIComponent.java:1779)
javax.faces.render.Renderer.encodeChildren(Renderer.java:168)
javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:845)
javax.faces.component.UIComponent.encodeAll(UIComponent.java:1779)
javax.faces.component.UIComponent.encodeAll(UIComponent.java:1782)
com.sun.faces.application.view.FaceletViewHandlingStrategy.renderView(FaceletViewHandlingStrategy.java:402)
com.sun.faces.application.view.MultiViewHandler.renderView(MultiViewHandler.java:125)
org.springframework.faces.webflow.FlowViewHandler.renderView(FlowViewHandler.java:99)
com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:121)
com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:139)
org.springframework.faces.mvc.JsfView.renderMergedOutputModel(JsfView.java:85)
org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:262)
org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1180)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:950)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:852)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:778)
javax.servlet.http.HttpServlet.service(HttpServlet.java:734)
javax.servlet.http.HttpServlet.service(HttpServlet.java:847)

ここで、特にSpringconfigのmessageSourceBean定義に何かが欠けている可能性があると思います。リソースバンドルを解決するときにTesteResBundleのメソッドが呼び出されないため、これが疑われます。

このテーマに関して提供された助けに感謝します。

4

1 に答える 1

1

私は実際に問題の回避策を作成することができました。私は春に最初の赤ちゃんのステップをしているので、「春の方法」で行われているとは思わないので、春の専門家がこのアプローチをレビューできればいいと思います。しかし、他のすべてが失敗した場合、私はこのそれほど美しくない回避策に固執します。

私は通常、構成アーティファクトを保持するシングルトンをWebアプリケーションに持っています。現在、実際にはSpringアプリケーションコンテキストへの参照を保持しているだけです。

public class ApplicationConfig {

    private static ApplicationConfig instance = new ApplicationConfig();

    private ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");

    private ApplicationConfig(){

    }

    public static ApplicationConfig instance(){
        return instance;
    }

    public ApplicationContext getApplicationContext(){
        return context;
    }
}

以前のリソースバンドルの実装を破棄し、代わりにAbstractMessageSourceを拡張しました。

public class TesteMessageSource extends AbstractMessageSource {

    @Override
    protected MessageFormat resolveCode(String key, Locale locale) {
        // This is just a dummy method.
        // It should lookup in a Map created in this same 
        // class for the correct key/locale resource value
        return createMessageFormat(key, Locale.US);
    }

    @Override
    protected String resolveCodeWithoutArguments(String key, Locale locale){
        // This is just a dummy method.
        // It should lookup in a Map created in this same 
        // class for the correct key/locale resource value
        return "dummyString";
    }
}

次に、カスタムELリゾルバーに渡される基本パラメーターが実際にはDelegatingMessageSourceのインスタンスであることに気付きました。Springのドキュメントによると:「すべての呼び出しを親MessageSourceに委任する空のMessageSource」。そこで、カスタムELリゾルバーを変更して、シングルトンからSpringアプリケーションコンテキストを取得し、messageResource Beanを取得して、DelegatingMessageSourceインスタンスの親MessageSourceとして設定しました。

public class MessageSourcePropertyResolver extends SpringBeanFacesELResolver /*implements MessageSourceAware */{

    public Object getValue(ELContext elContext, Object base, Object property)
        throws ELException {

        if (base instanceof MessageSource && property instanceof String) {
            DelegatingMessageSource delegatingMessageSource = (DelegatingMessageSource) base;
            BeanFactory factory = ApplicationConfig.instance().getApplicationContext();
            MessageSource messageSource = (MessageSource) factory.getBean("messageSource");
            delegatingMessageSource.setParentMessageSource(messageSource);

            String result = delegatingMessageSource.getMessage((String) property, new Object[] {}, getLocale());
            if (result != null) {
                elContext.setPropertyResolved(true);
            }

            return result;
        }

        return super.getValue(elContext, base, property);
    }

    private Locale getLocale() {
        FacesContext context = FacesContext.getCurrentInstance();
        return context.getExternalContext().getRequestLocale();
    }
}

SpringmessageSourceBeanの構成は次のようになりました。

<bean id="messageSource" class="pt.teste.pojo.TesteMessageSource">
</bean>

Faceletsxtmlコンポーネントのメッセージリソースアクセスは次のようになりました。

<h:outputText value="${messageSource['prop.aaa']}" />

ここで、prop.aaaは「property」パラメーターとしてカスタムELリゾルバーに渡され、その「property」と、リゾルバーにも渡されるロケールを検索する必要があります。

このようにしてすべてが機能しますが、これは、構成済みのMessageSourceをカスタムELリゾルバーに渡すなど、より適切で正しい方法で実行できるとほぼ確信しています。これは、カスタムMessageSourceのインスタンス、または少なくとも親がカスタムMessageSourceとして設定されているデフォルトのMessageSourceを渡すことを意味します。

于 2012-09-07T20:57:27.567 に答える