[英]JSF - Resource Bundle from Spring bean and database
這是許多論壇中提出的主題,但我找不到任何准確而具體的答案。 在我看來,即使是公認的答案也是不完整的,所以我會嘗試發布完整的嘗試來解決這個問題,希望能就這個問題建立一個精確的問題+答案。
我正在嘗試讓Resource Bundles在JSF中運行。 資源包來自Spring bean,應該從任意外部系統(即數據庫)加載。
我現在將繞過數據庫查詢並使用模擬的資源包來保持清晰。
這是我的資源包業務實現,我設法從這個論壇的其他帖子收集:
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解析器,我也在論壇中抓住了。 如果base是MessageSource的實例,它會嘗試收集消息。 如果沒有,它會將分辨率傳遞給默認的Spring EL解析器:
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配置中,我將messageSource bean定義為:
<bean id="messageSource" class="pt.teste.pojo.TesteResBundle">
</bean>
我可以確認messageSource bean是否正確實例化,並且在應用程序啟動時正確加載了HashMap。 我可以確認正在調用自定義處理程序,並且正在將所有不是資源消息的EL傳遞給默認的Spring解析器並正在正確解析。
當我在xhtml JSF 2.0頁面中使用Resource Bundle時,我這樣做:
<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)
我認為我可能在這里遺漏了一些東西,特別是在Spring配置中的messageSource bean定義中。 我懷疑這是因為在解析資源包時沒有調用TesteResBundle的方法。
感謝您就此主題提供的任何幫助。
我實際上設法為這個問題制定了一個解決方法。 因為我正在春天做我的第一個嬰兒步驟,如果Spring專家可以審查這種方法會很好,因為我不認為它是以“Spring方式”完成的。 但如果其他一切都失敗了,我會堅持這個不那么漂亮的解決方法。
我的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;
}
}
我放棄了之前的Resource Bundle實現,而是擴展了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解析器的base參數實際上是DelegatingMessageSource的一個實例。 根據Spring文檔:“將所有調用委托給父MessageSource的空MessageSource”。 所以我修改了自定義EL解析器以從單例獲取Spring應用程序上下文,然后獲取messageResource bean並將其設置為父MessageSource到DelegatingMessageSource實例:
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();
}
}
Spring messageSource bean配置成為:
<bean id="messageSource" class="pt.teste.pojo.TesteMessageSource">
</bean>
Facelets xtml組件中的消息資源訪問變為:
<h:outputText value="${messageSource['prop.aaa']}" />
其中prop.aaa作為“屬性”參數傳遞給自定義EL解析器,我們只需要查找該“屬性”和也傳遞給解析器的Locale。
這樣一切都正常,但我幾乎可以肯定這可以通過更好更正確的方式完成,比如將已經配置的MessageSource傳遞給自定義EL解析器。 有了這個,我的意思是傳遞自定義MessageSource的實例,或者至少是一個默認的MessageSource,其父節點已經設置為自定義MessageSource。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.