首页 > 代码库 > [Spring MVC] - 从数据库读取MessageSource

[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>

 

将看到:

 

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

 

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

resource.getMessage("valid.ageMin", new Object[]{"age",18}, request.getLocale())

 

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