[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())
时间: 2024-08-15 23:23:42

[Spring MVC] - 从数据库读取MessageSource的相关文章

spring mvc 添加数据库数据同时获取刚刚生成的数据ID

mapper.xml文件 <!-- 添加信息 --> <insert id="insert" parameterType="实体类" useGeneratedKeys="true" keyProperty="id"> <!-- 添加送达大信息的同时获取自动生成的ID的值 --> <selectKey resultType="int" keyProperty=&quo

Spring MVC程序中怎么得到静态资源文件css,js,图片文件的路径问题

问题描述 在用springmvc开发应用程序的时候.对于像我一样的初学者,而且还是自学的人,有一个很头疼的问题.那就是数据都已经查出来了,但是页面的样式仍然十分简陋,加载不了css.js,图片等资源文件,如果你输入某个css的文件在浏览器中得到的直接是404错误(例如http://localhost:8080/iMax_view/css/classify.css),表示路径肯定没有错,这是因为你在web.xml中配置的springservlet ]<!-- springmvc前端控制器,rest

Spring MVC程序中得到静态资源文件css,js,图片文件的路径问题总结

上一篇 | 下一篇 Spring MVC程序中得到静态资源文件css,js,图片 文件的路径 用 Spring MVC 开发应用程序,对于初学者有一个很头疼的问题,那就是程序数据都已经查询出来了,但界面样式仍然十分丑陋,加载不了 css,js,图片等资源文件.当你在浏览器上直接输入某个css文件的路径时,直接得到404错误,而路径肯定没有错,其原因就在于在web.xml 中配置了类似如下的 spring servlet: 程序代码 <servlet>    <servlet-name&g

Spring MVC程序中得到静态资源文件css,js,图片

转载自:http://www.blogjava.net/fiele/archive/2014/08/24/417283.html 用 Spring MVC 开发应用程序,对于初学者有一个很头疼的问题,那就是程序数据都已经查询出来了,但界面样式仍然十分丑陋,加载不了 css,js,图片等资源文件.当你在浏览器上直接输入某个css文件的路径时,直接得到404错误,而路径肯定没有错,其原因就在于在web.xml 中配置了类似如下的 spring servlet: 程序代码 <servlet>    

Spring MVC基础知识整理?Spring+SpringMVC+Hibernate整合操作数据库

概述 Hibernate是一款优秀的ORM框架,能够连接并操作数据库,包括保存和修改数据.Spring MVC是Java的web框架,能够将Hibernate集成进去,完成数据的CRUD.Hibernate使用方便,配置响应的XML文件即可.由于spring3.x,基于asm的某些特征,而这些asm还没有用jdk8编译,所以采用Spring 3+JDK8就会报错,提示错误信息( java.lang.IllegalArgumentException),具体解决方案有:1.Spring 3+JDK7

[转]asp.net mvc 从数据库中读取图片

本文转自:http://www.cnblogs.com/mayt/archive/2010/05/20/1740358.html 首先是创建一个类,继承于ActionResult,记住要引用System.Web.Mvc命名空间,如下: public class ImageResult : ActionResult { public ImageFormat ContentType { get; set; } public Image image { get; set; } public strin

Spring利用propertyConfigurer类 读取.property数据库配置文件

1.Spring的框架中,org.springframework.beans.factory.config.PropertyPlaceholderConfigurer类可以将.properties(key/value形式)文件中 一些动态设定的值(value),在XML中替换为占位该键($key$)的值, .properties文件可以根据客户需求,自定义一些相关的参数,这样的设计可提供程序的灵活性. 2.在Spring中,使用PropertyPlaceholderConfigurer可以在XM

Spring mvc整合mybatis基于mysql数据库实现用户增删改查及其分页显示的完整入门实例【转】

Spring mvc整合mybatis例子, 基于mysql数据库实现对用户的增.删.改.查,及分页显示的完整例子. 查询显示用户 添加用户 更新用户 官方验证: 项目截图 必须修改applicationContext.xml中mysql的配置为本地的,否则启动失败. 另外jar包多一个ehcache.jar无关紧要,删除即可. 1. 使用阿里巴巴Druid连接池(高效.功能强大.可扩展性好的数据库连接池.监控数据库访问性能.支持Common-Logging.Log4j和JdkLog,监控数据库

Spring MVC篇二、使用JdbcTemplate进行数据库操作

上一篇只是一个简单的Spring MVC框架,接下来添加一些跟数据库的交互. 一.添加jdbc相关配置 在maven中添加相关依赖后,配置数据库访问参数及数据源.数据库参数使用配置文件,代码如下: jdbc.properties 1 jdbc.driver=com.mysql.jdbc.Driver 2 jdbc.url=jdbc:mysql://localhost:3306/sampledb 3 jdbc.username=root 4 jdbc.password=123456 5 jdbc.