message_zh_CN.properties(中文简体) message_en_US.properties(美国英文) 等等中配置key=value 使用配置的配置 @Autowired private MessageSource messageSource; messageSource.getMessage(key,null,"default value",Locale.getDefault()); //如果没有配置,会返回默认值 messageSource.getMessage(key,null,Locale.getDefault()); //如果没有配置会抛出异常 String getMessage(String code, Object/[/] args, String defaultMessage, Locale locale); String getMessage(String code, Object/[/] args, Locale locale) throws NoSuchMessageException;
上述国际化使用适合做小型的web项目,配置key不多,或者项目的使用国家或地区很少。
如果配置项非常多的话,那么properties文件将会非常大,而且配置项非常多。如果支持全世界所有国家和地区时,那么就要有许多个properties文件,能够使用,但相对比较麻烦。
针对上述缺点,能否将上述的配置key及value写入数据中了?
在数据库中定义表,表中基础字段国家或地区代表符号,如zh_CN(简体中文),en_US(美国英语)等,key及value,然后再增加一个basename,这个字段是参考spring源码做的,下面详细讲解。
创建sql
CREATE TABLE t_messages ( `basename` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `locale_lang` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `k` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `v` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL ) ; basename基础文件目录 locale_lan国家或地区代表符号 k key值 v value值
a方案
对待该表就想对待普通的业务表,针对该表建立MessageMapper文件,创建针对该表的增删改查方法,在service层控制事务管理及缓存等等,当任何一个controller需要国际化返回结果的时候,就可以通过调MessageService方法来得到对应的value.
此处mapper里面的方法仅仅是举例,详细的可根据项目需求自己修改 @Mapper public interface MessageMapper { String columnList = " basename,locale_lan,k,v "; @Insert("insert into t_message(" +columnList+") values(#{basename},#{locale_lan},#{k},#{v})") boolean insertMessage(Message message); @Select("select " + columnList + "from t_message where basename = #{basename} and locale_lan = #{language}") AddressEntity getMessage(String basename,String language); }
b方案
a方案是一种实现方法,但是spring既然已经实现properties文件的具体用法,对spring进行二次开发,开发适合自己项目的业务,岂不是更好吗?
spring 实现国际化源码简单分析 MessageSource接口 public interface MessageSource { String getMessage(String code, Object[] args, String defaultMessage, Locale locale); String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException; String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException; } HierarchicalMessageSource接口 其继承了MessageSource接口,增加了父MessageSource的设置方法 public interface HierarchicalMessageSource extends MessageSource { void setParentMessageSource(MessageSource parent); MessageSource getParentMessageSource(); } DelegatingMessageSource类 该类主要是委派父MessageSource处理getMessage(); public class DelegatingMessageSource extends MessageSourceSupport implements HierarchicalMessageSource; @Override public String getMessage(String code, Object[] args, String defaultMessage, Locale locale) { if (this.parentMessageSource != null) { return **this.parentMessageSource.getMessage(code, args, defaultMessage, locale);** } else { **return renderDefaultMessage(defaultMessage, args, locale);** } } @Override public String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException { if (this.parentMessageSource != null) { **return this.parentMessageSource.getMessage(code, args, locale);** } else { throw new NoSuchMessageException(code, locale); } } @Override public String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException { if (this.parentMessageSource != null) { **return this.parentMessageSource.getMessage(resolvable, locale);** } else { if (resolvable.getDefaultMessage() != null) { **return renderDefaultMessage(resolvable.getDefaultMessage(), resolvable.getArguments(), locale);** } String[] codes = resolvable.getCodes(); String code = (codes != null && codes.length > 0 ? codes[0] : null); throw new NoSuchMessageException(code, locale); } } MessageSourceSupport 该类为获取字符串后的格式化处理的类 private final Map<String, Map<Locale, MessageFormat>> messageFormatsPerMessage = new HashMap(); 格式化存储的结构类型,大Map的key为basename,嵌套的Map,key为Locale,value为MessageFormat,不同地区可以设置不同的格式化样式 核心方法:return messageFormat.format(this.resolveArguments(code, locale)); this.resolveArguments(code, locale)方法获取对应的value AbstractMessageSource为MessageSource接口的核心实现类 核心方法的实现方法都是final的,不允许重写 public final String getMessage(String code, Object[] args, String defaultMessage, Locale locale) ; public final String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException; AbstractResourceBasedMessageSource继承了AbstractMessageSource类 AbstractResourceBasedMessageSource的实现类有 1.ResourceBundleMessageSource 2.ReloadableResourceBundleMessageSource 3.ReloadableResourceBundleMessageSource 关注的主要是ResourceBundleMessageSource类 当调用ResourceBundleMessageSource 对象的getMessage()方法时候 其会调用其的protected ResourceBundle doGetBundle(String basename, Locale locale) throws MissingResourceException { return ResourceBundle.getBundle(basename, locale, this.getBundleClassLoader(), new ResourceBundleMessageSource.MessageSourceControl()); }方法,所以重写该方法可以实现自我配置的类。
b方案具体实现,具体的查库方法没有贴出来
public class DatabaseAndResourceBundleMessageSource2 extends ResourceBundleMessageSource { private static final Logger log = LoggerFactory.getLogger(DatabaseAndResourceBundleMessageSource2.class); @Autowired DataSource dataSource; @Autowired MessageDBHandler databaseMessageHandler; @Override protected ResourceBundle doGetBundle(String basename, Locale locale) throws MissingResourceException { try { ResourceBundle delegate = super.doGetBundle(basename, locale);//父类MessageSource实现 return new ResourceBundle() { @Override protected Object handleGetObject(String key) { return Optional.<Object>ofNullable(databaseMessageHandler.loadMessageFromDb(basename, locale.getLanguage(), key)).orElseGet(() -> delegate.getObject(key)); //依据key获取value } @Override public Enumeration<String> getKeys() { log.info("getKeys"); Set<String> l = databaseMessageHandler.loadKeysFromDb(basename, locale.getLanguage()); l.addAll(delegate.keySet()); return Collections.enumeration(l); } }; } catch (MissingResourceException e) { return new ResourceBundle() { @Override protected Object handleGetObject(String key) { return databaseMessageHandler.loadMessageFromDb(basename, locale.getLanguage(), key); } @Override public Enumeration<String> getKeys() { return Collections.enumeration(databaseMessageHandler.loadKeysFromDb(basename, locale.getLanguage())); //获取同一basename和language下的所有keys } }; } } } @Bean("messageSource") public MessageSource myMessageSource(){ ResourceBundleMessageSource messageSource = new DatabaseAndResourceBundleMessageSource2(); String[] baseNames = new String[2]; baseNames[0] = "app/notice"; baseNames[1] = "app/mail"; messageSource.setBasenames(baseNames); return messageSource; } 注入MessageSource