业务层资源国际化处理
- - CSDN博客推荐文章用过struts2等mvc框架开发的同学都知道,使用struts2处理国际化的消息非常简单直观,但是mvc框架的定位是在展示层(jsp,action)等,在一个典型的3层结构中,处于最上层的位置,按照分层设计原则,下层组件是不可以调用上层组件的,这样就存在一个问题,我们在业务层中可能也会出现一些需要国际化处理的消息信息,这些信息如何设置呢.
用过struts2等mvc框架开发的同学都知道,使用struts2处理国际化的消息非常简单直观,但是mvc框架的定位是在展示层(jsp,action)等,在一个典型的3层结构中,处于最上层的位置,按照分层设计原则,下层组件是不可以调用上层组件的,这样就存在一个问题,我们在业务层中可能也会出现一些需要国际化处理的消息信息,这些信息如何设置呢?
在这篇文章中,我们将借鉴struts2的国际化处理机制,但是要比struts2简单的多,因为业务层需要国际化处理的消息毕竟是少数,废话不多说,直接上干货
先举个例子-业务层未国际化处理前的代码
@Override public ExecuteResult<User> login(String userName, String password) { ExecuteResult<User> executeResult = new ExecuteResult<User>(); User userInfo = userDAO.getUserByName(userName); if(userInfo == null){ executeResult.addErrorMessage("不存在的用户名"); return executeResult; } if(!userInfo.getPassword().equals(password)){ executeResult.addErrorMessage("密码错误"); return executeResult; } executeResult.setResult(userInfo); return executeResult; }设计实现:
1.添加国际化消息拦截器
我们的应用是基于struts2的,struts2默认提供了一个i18n拦截器,这里我们扩展了以下该拦截器的功能,将locale信息保存起来
/** * 扩展struts2默认的i18n拦截器,添加设置local到LocaleContextHolder中的功能 * @author WangXuzheng * @see com.opensymphony.xwork2.interceptor.I18nInterceptor * @see org.springframework.context.i18n.LocaleContextHolder */ public class I18nResolverInterceptor extends I18nInterceptor { private static final long serialVersionUID = 5888969294461266478L; @Override protected void saveLocale(ActionInvocation invocation, Locale locale) { super.saveLocale(invocation, locale); LocaleContextHolder.setLocale(locale); } }2.在struts.xml中替换默认的i18n拦截器
<interceptor name="i18n" class="com.haier.openplatform.i18n.interceptor.I18nResolverInterceptor"/>3.设计业务层国际化资源解析器接口
/** * 国际化资源处理器 * @author WangXuzheng * */ public interface I18nResolver { /** * 设置要进行资源处理的目标类 * @param clazz */ @SuppressWarnings("rawtypes") public void setClass(Class clazz); /** * 解析国际化资源文件,如果找不到该code,返回默认的消息 * @param code i18n资源的key值 * @return 如果找到返回具体的消息值,如果不存在返回默认消息 * @see java.text.MessageFormat */ String getMessage(String code); /** * 解析国际化资源文件,如果找不到该code,返回默认的消息 * @param code i18n资源的key值 * @return 如果找到返回具体的消息值,如果不存在返回默认消息 * @see java.text.MessageFormat */ String getMessage(String code,String arg); /** * 解析国际化资源文件,如果找不到该code,返回默认的消息 * @param code i18n资源的key值 * @param args 资源中的变量值 * @param defaultMessage 默认消息 * @return 如果找到返回具体的消息值,如果不存在返回默认消息 * @see java.text.MessageFormat */ String getMessage(String code, Object[] args, String defaultMessage); /** * 解析国际化资源文件查找指定code对应的消息,如果不存在,抛出异常 * @param code * @param args * @return * @throws MessageNotFoundException * @see java.text.MessageFormat * @throws MessageNotFoundException */ String getMessage(String code, Object[] args); }默认的实现类-这里我们的资源配置文件放在和待处理的类同文件下同名的.properties文件中
例如:java类
com.haier.openplatform.showcase.security.service.impl. UserServiceImpl
对应的资源文件为
com/haier/openplatform/showcase/security/service/impl/UserServiceImpl_zh_CN.properties
com/haier/openplatform/showcase/security/service/impl/UserServiceImpl_en_US.properties
/** * 默认的资源文件解析器,该类读取<code>org.springframework.context.i18n.LocaleContextHolder</code>中保存的Local信息解析资源文件 * @author WangXuzheng * @see org.springframework.context.i18n.LocaleContextHolder */ public class DefaultI18nResolver extends MessageSourceSupport implements I18nResolver { private static final ConcurrentMap<String, ResourceBundle> BUNDLE_MAP = new ConcurrentHashMap<String, ResourceBundle>(); private static final List<String> DEFAULT_RESOURCE_BUNDLES = new CopyOnWriteArrayList<String>(); private static final Log LOG = LogFactory.getLog(DefaultI18nResolver.class); @SuppressWarnings("rawtypes") protected Class clazz; /** * 添加全局资源配置信息 * * @param resourceBundleName the name of the bundle to add. */ public static void addDefaultResourceBundle(String resourceBundleName) { //make sure this doesn't get added more than once synchronized (DEFAULT_RESOURCE_BUNDLES) { DEFAULT_RESOURCE_BUNDLES.remove(resourceBundleName); DEFAULT_RESOURCE_BUNDLES.add(0, resourceBundleName); } if (LOG.isDebugEnabled()) { LOG.debug("Added default resource bundle '" + resourceBundleName + "' to default resource bundles = " + DEFAULT_RESOURCE_BUNDLES); } } /** * Creates a key to used for lookup/storing in the bundle misses cache. * * @param aBundleName the name of the bundle (usually it's FQN classname). * @param locale the locale. * @return the key to use for lookup/storing in the bundle misses cache. */ private String createMissesKey(String aBundleName, Locale locale) { return aBundleName + "_" + locale.toString(); } /** * 从全局资源文件中读取文案信息 * * @param aTextName 文案 key * @param locale the locale the message should be for * @return */ private String findDefaultText(String aTextName, Locale locale) { List<String> localList = DEFAULT_RESOURCE_BUNDLES; for (String bundleName : localList) { ResourceBundle bundle = findResourceBundle(bundleName, locale); if (bundle != null) { try { return bundle.getString(aTextName); } catch (MissingResourceException e) { // ignore and try others } } } return null; } /** * 根据资源名称和locale信息查找资源信息 * @param aBundleName the name of the bundle (usually it's FQN classname). * @param locale the locale. * @return the bundle, <tt>null</tt> if not found. */ protected ResourceBundle findResourceBundle(String aBundleName, Locale locale) { String key = createMissesKey(aBundleName, locale); ResourceBundle bundle = BUNDLE_MAP.get(key); if (bundle == null) { bundle = ResourceBundle.getBundle(aBundleName, locale, Thread.currentThread().getContextClassLoader()); BUNDLE_MAP.put(key, bundle); } return bundle; } private Locale getLocale() { return LocaleContextHolder.getLocale(); } @Override public String getMessage(String code) { return getMessage(code, new Object[] {}); } /** * 获取资源消息对应的值,先从指定的bundleName的资源中获取文案,如果找不到,从globalResources中读取 * @param bundleName * @param locale * @param key * @param args * @return * @see #findResourceBundle */ private String getMessage(String bundleName, Locale locale, String key, Object[] args) { ResourceBundle bundle = findResourceBundle(bundleName, locale); if (bundle == null) { return null; } String orginalMessage = null; try { orginalMessage = bundle.getString(key); } catch (MissingResourceException e) { // read text from global resources orginalMessage = findDefaultText(bundleName, locale); } return this.formatMessage(orginalMessage, args, locale); } @Override public String getMessage(String code, Object[] args) { return getMessage(resolveBunFile(), getLocale(), code, args); } @Override public String getMessage(String code, Object[] args, String defaultMessage) { return StringUtils.defaultIfBlank(getMessage(code, args), defaultMessage); } @Override public String getMessage(String code, String arg) { String[] args = new String[] { arg }; return getMessage(code, args); } protected String resolveBunFile() { String pack = this.clazz.getName(); return pack.replaceAll("[.]", "/"); } @SuppressWarnings("rawtypes") public void setClass(Class clazz) { this.clazz = clazz; } }加一个静态工厂类来获取解析器
/** * 资源解析器工厂类 * @author WangXuzheng * */ public final class I18nResolverFactory { @SuppressWarnings("rawtypes") private static final ConcurrentMap<Class, I18nResolver> I18N_RESOVER_MAP = new ConcurrentHashMap<Class, I18nResolver>(); private I18nResolverFactory(){ } @SuppressWarnings("rawtypes") public static I18nResolver getDefaultI18nResolver(Class clazz){ I18nResolver resolver = I18N_RESOVER_MAP.get(clazz); if(resolver == null){ resolver = new DefaultI18nResolver(); resolver.setClass(clazz); I18N_RESOVER_MAP.put(clazz, resolver); } return resolver; } }4.调用实现-非常类似log4j的调用方式
public class UserServiceImpl implements UserService { private static final I18nResolver I18N_RESOLVER = I18nResolverFactory.getDefaultI18nResolver(UserServiceImpl.class); private UserDAO userDAO; private RoleDAO roleDAO; @Override public ExecuteResult<User> login(String userName, String password) { ExecuteResult<User> executeResult = new ExecuteResult<User>(); User userInfo = userDAO.getUserByName(userName); if(userInfo == null){ executeResult.addErrorMessage( I18N_RESOLVER.getMessage("user.notexisted")); return executeResult; } if(!userInfo.getPassword().equals(password)){ executeResult.addErrorMessage(I18N_RESOLVER.getMessage("user.wrongpassword")); return executeResult; } executeResult.setResult(userInfo); return executeResult; }