[Spring MVC] - 从数据库读取MessageSource

Spring MVC中使用MessageSource默认是写在properties文件当中,以支持国际化。

但很多时候我们需要把数据写到数据库当中,而不是在properties文件当中,以方便日常维护。

1、先看Spring配置

    <!-- 默认的注解映射的支持 -->
<mvc:annotation-driven validator="validator" conversion-service="conversionService" /> <!-- 资源文件 -->
<bean id="propertiesMessageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>resource</value>
<value>validation</value>
</list>
</property>
</bean>
<bean id="databaseMessageSource" class="com.obs2.util.MessageResource">
<property name="parentMessageSource" ref="propertiesMessageSource"/>
</bean>
<bean id="messageInterpolator" class="com.obs2.util.MessageResourceInterpolator">
<property name="messageResource" ref="databaseMessageSource"/>
</bean>
<!-- 验证器 -->
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="messageInterpolator" ref="messageInterpolator"/>
</bean>

这里定义了一个propertiesMessageSource,一个databaseMessageSourcer,和一个messageInterpolator。

propertiesMessageSource用于读取properties文件

databaseMessageSourcer用于读取数据库的数据配置,其中,有一个属性设置它的父MessageSource为propertiesMessageSource。意思是如果数据库找不到对应的数据,到properties文件当中查找。

messageInterpolator是个拦截器。

2、数据库的POJO定义:

package com.obs2.dao.impl.bean;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table; @Entity
@SuppressWarnings("serial")
@Table(name="resource")
public class Resource implements Serializable { @Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="resource_id")
private long resourceId; @Column(name="name", length=50, nullable=false)
private String name; @Column(name="text", length=1000, nullable=false)
private String text; @Column(name="language", length=5, nullable=false)
private String language; public long getResourceId() {
return resourceId;
}
public void setResourceId(long resourceId) {
this.resourceId = resourceId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String getLanguage() {
return language;
}
public void setLanguage(String language) {
this.language = language;
} }

定义了一张表[resource],字段有:[resource_id]、[name]、[text]、[language]

具体的DAO、Service操作方法这里忽略不写了。

3、读取数据库的MessageResource类

package com.obs2.util;

import java.text.MessageFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map; import javax.annotation.Resource; import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.support.AbstractMessageSource;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.ResourceLoader; import com.obs2.service.ResourceService; /**
* 取得资源数据
* @author Robin
*
*/
public class MessageResource extends AbstractMessageSource implements ResourceLoaderAware { @SuppressWarnings("unused")
private ResourceLoader resourceLoader; @Resource
private ResourceService resourceService; /**
* Map切分字符
*/
protected final String MAP_SPLIT_CODE = "|"; protected final String DB_SPLIT_CODE = "_"; private final Map<String, String> properties = new HashMap<String, String>(); public MessageResource() {
// reload();
} public void reload() {
properties.clear();
properties.putAll(loadTexts());
} protected Map<String, String> loadTexts() {
Map<String, String> mapResource = new HashMap<String, String>();
List<com.obs2.service.bean.Resource> resources = resourceService.findAll();
for (com.obs2.service.bean.Resource item : resources) {
String code = item.getName() + MAP_SPLIT_CODE + item.getLanguage();
mapResource.put(code, item.getText());
}
return mapResource;
} private String getText(String code, Locale locale) {
String localeCode = locale.getLanguage() + DB_SPLIT_CODE + locale.getCountry();
String key = code + MAP_SPLIT_CODE + localeCode;
String localeText = properties.get(key);
String resourceText = code; if(localeText != null) {
resourceText = localeText;
}
else {
localeCode = Locale.ENGLISH.getLanguage();
key = code + MAP_SPLIT_CODE + localeCode;
localeText = properties.get(key);
if(localeText != null) {
resourceText = localeText;
}
else {
try {
if(getParentMessageSource() != null) {
resourceText = getParentMessageSource().getMessage(code, null, locale);
}
} catch (Exception e) {
logger.error("Cannot find message with code: " + code);
}
}
}
return resourceText;
} @Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = (resourceLoader != null ? resourceLoader : new DefaultResourceLoader());
} @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) {
String result = getText(code, locale);
return result;
} }

主要是重载AbstractMessageSource和ResourceLoaderAware,以实现Spring MVC的MessageSource国际化调用。

类中的reload()方法,我把它写到了一个ServletListener当中,让项目启动时,自动加载数据到static的map中。

4、这是Listener:

package com.obs2.util;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.http.HttpSessionEvent; import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils; /**
* 系统启动监听
* @author Robin
*
*/
public class SystemListener implements ServletContextListener { /**
* context初始化时激发
*/
@Override
public void contextInitialized(ServletContextEvent e) {
//------------------------------------------------------------
// 取得ServletContext
//------------------------------------------------------------
ServletContext context = e.getServletContext();
WebApplicationContext applicationContext = WebApplicationContextUtils .getWebApplicationContext(context); //------------------------------------------------------------
// 设置国际化多语言
//------------------------------------------------------------
MessageResource messageSource = applicationContext.getBean(MessageResource.class);
messageSource.reload();
} /**
* context删除时激发
*/
@Override
public void contextDestroyed(ServletContextEvent e) {
} /**
* 创建一个 session时激发
* @param e
*/
public void sessionCreated(HttpSessionEvent e) {
} /**
* 当一个 session失效时激发
* @param e
*/
public void sessionDestroyed(HttpSessionEvent e) {
} /**
* 设置 context的属性,它将激发attributeReplaced或attributeAdded方法
* @param e
*/
public void setContext(HttpSessionEvent e) {
} /**
* 增加一个新的属性时激发
* @param e
*/
public void attributeAdded(ServletContextAttributeEvent e) {
} /**
* 删除一个新的属性时激发
* @param e
*/
public void attributeRemoved(ServletContextAttributeEvent e) {
} /**
* 属性被替代时激发
* @param e
*/
public void attributeReplaced(ServletContextAttributeEvent e) {
} }

当然了,Listener需要加入到web.xml当中:

    <!-- 系统启动监听 -->
<listener>
<listener-class>com.obs2.util.SystemListener</listener-class>
</listener>

4、Interceptor拦截器

package com.obs2.util;

import java.text.MessageFormat;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry; import javax.annotation.Resource;
import javax.validation.MessageInterpolator; import org.springframework.binding.message.MessageBuilder; /**
* 拦截Annotation验证信息
* @author Robin
*
*/
public class MessageResourceInterpolator implements MessageInterpolator { @Resource
private MessageResource messageResource;
public void setMessageResource(MessageResource messageResource) {
this.messageResource = messageResource;
} @Override
public String interpolate(String messageTemplate, Context context) { String messageTemp = null;
if(messageTemplate.startsWith("{") && messageTemplate.endsWith("}")) {
messageTemp = messageTemplate.substring(1, messageTemplate.length() - 1);
}
else {
return messageTemplate;
} String[] params = (String[]) context.getConstraintDescriptor().getAttributes().get("params"); MessageBuilder builder = new MessageBuilder().code(messageTemp);
if (params != null) {
for (String param : params) {
builder = builder.arg(param);
}
} String result = builder.build().resolveMessage(messageResource, Locale.ENGLISH).getText();
return result;
} @Override
public String interpolate(String messageTemplate, Context context, Locale locale) { String messageTemp = null;
if(messageTemplate.startsWith("{") && messageTemplate.endsWith("}")) {
messageTemp = messageTemplate.substring(1, messageTemplate.length() - 1);
}
else {
return messageTemplate;
} String[] params = (String[]) context.getConstraintDescriptor().getAttributes().get("params"); MessageBuilder builder = new MessageBuilder().code(messageTemp);
if (params != null) {
builder = builder.args(params);
} String result = builder.build().resolveMessage(messageResource, locale).getText();
return result;
} }

事实上,不使用拦截器,上面的数据库MessageSource类已经可以工作了。但在使用hibernate的Annotation的validator时,不加入拦截器,是不行的,它不会触发。

这类里调用了一个jar包:spring-binding-2.3.1.RELEASE.jar

自行上网搜搜下载。

一切完成,测试下,写一个domain entity:

package com.obs2.controller.bean;

import javax.validation.constraints.Min;

import org.hibernate.validator.constraints.NotEmpty;

import com.obs2.controller.validator.UserName;

public class Account {

    @UserName(format="^[\\w_]+$", message="{valid.userName}", min=6, max=30)
private String userName;
@NotEmpty(message="{valid.required}")
private String password;
@Min(value=18, message="{valid.ageMin}")
private int age; public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
} }

写一个简单的view(这里使用的是freemarker)

<#assign c=JspTaglibs["http://java.sun.com/jsp/jstl/core"] />
<#assign fmt=JspTaglibs["http://java.sun.com/jsp/jstl/fmt"] />
<#assign fn=JspTaglibs["http://java.sun.com/jsp/jstl/functions"] />
<#assign st=JspTaglibs["http://www.springframework.org/tags"] />
<#assign form=JspTaglibs["http://www.springframework.org/tags/form"] />
<head>
<title>Login</title>
</head> <body>
<@form.form action="${request.contextPath}/passport/login" method="post" modelAttribute="account">
User name:<@form.input path="userName"/><@form.errors path="userName"/><br/>
Password:<@form.password path="password"/><@form.errors path="password" /><br/>
Age:<@form.input path="age"/><@form.errors path="age" /><br/>
<input type="submit" value="Login" />
</@form.form>
</body>

将看到:

[Spring MVC] - 从数据库读取MessageSource

这种方法还有一个小BUG,就是在使用annotation validator时,如果使用了替换符,似乎不起作用。暂时没研究出来,谁搞出来了,麻烦留个言提示下。so thx.

代码中直接调用,可以这样写:

resource.getMessage("valid.ageMin", new Object[]{"age",18}, request.getLocale())
上一篇:JAVA多线程suspend()、resume()和wait()、notify()的区别


下一篇:关于Videodownload helper的下载问题