一、简介
1、Spring消息体系的定义:
①消息取得接口:MessageSource、HierarchicalMessageSource
②消息参数接口:MessageSourceResolvable
2、Spring消息体系的实现:
①消息取得:AbstractMessageSource、ResourceBundleMessageSource、ReloadableResourceBundleMessageSource、StaticMessageSource
②消息参数:DefaultMessageSourceResolvable、FieldError、ObjectError
3、Spring消息体系的使用:
①ApplicationContext、MessageSourceAware
二、Spring消息体系的定义
1、MessageSource:
定义个三个getMessage操作:
- 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;
Spring为一个取得消息的操作,提供了三个方法,细化了取消息操作的不同需求。一种提供默认消息,不报异常;一种不提供默认消息,报NoSuchMessageException;一种使用MessageSourceResolvable封装需要的参数,虽然封装的参数中提供了默认消息,但为防止提供了无效的默认消息,还是报了NoSuchMessageException。
2、HierarchicalMessageSource:
继承MessageSource接口,添加了两个操作:
- void setParentMessageSource(MessageSource parent);
- MessageSource getParentMessageSource();
Spring通过这两个方法提供了一种实现MessageSource层次的定义
3、MessageSourceResolvable:
为了方便MessageSource的使用,Spring定义了取消息使用的参数对象,提供了三个操作:
- public String[] getCodes();
- public Object[] getArguments();
- public String getDefaultMessage();
4、接口定义中的智慧
①MessageSource与HierarchicalMessageSource接口的分离,为两个不同的目的都定义了清晰的接口。虽然了Spring的MessageSource的实现都继承与HierarchicalMessageSource,当时因为MessageSource接口的存在,可以使ApplicationContext接口直接提供一个标准的取得消息的方法集,而不用去关心消息体系的具体实现。
②提供MessageSourceResolvable接口,方便消息的使用。取得消息时不需要自己组织多个参数,而是直接将一个MessageSourceResolvable的实现传入即可。
③MessageSourceResolvable接口为什么不提供getLocal()操作?国际化应该是与业务实现无关,不应该是由业务实现去考虑。最好通过配置,或其他方法自动取得。
三、Spring消息体系的实现
1、AbstractMessageSource
①实现了HierarchicalMessageSource接口,提供了MessageSource的一个基本的抽象实现,并提供protected abstract MessageFormat resolveCode(String code, Locale locale);方法,留给子类自己决定自己的国际化实现。
②提供的属性:
- private boolean useCodeAsDefaultMessage = false;
定义当没有提供Default Message时,是否使用Message Code作为Default Message。
- private boolean alwaysUseMessageFormat = false;
当没有消息参数时,是否还是使用MessageFormat。当消息中包含一些需要MessageFormat特殊处理的逃逸字符时,需要设置为true。
③实现操作祥解:
- void setParentMessageSource(MessageSource parent);
- MessageSource getParentMessageSource();
④实现了消息的层次操作,并将父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;
所有取得消息的操作都有getMessageInternal()方法实现。根据默认消息取得默认消息时,使用renderDefaultMessage()方法。无默认消息根据useCodeAsDefaultMessage属性,取得默认消息时使用getDefaultMessage()方法。
⑤getMessageInternal()方法的实现:
- 无消息参数,且alwaysUseMessageFormat为false时,调用resolveCodeWithoutArguments()方法直接取得消息。其实在resolveCodeWithoutArguments()方法的实现中还是使用MessageFormat,所以Spring建议在子类中覆盖resolveCodeWithoutArguments()方法。
- 否则,先调用resolveArguments()方法将参数中的MessageSourceResolvable实例转化为对应的消息,然后调用resolveCode()取得MessageFormat,之后使用MessageFormat处理消息。
- 当没有找到消息时,调用getMessageFromParent()方法,从父MessageSource中查找消息。
⑥cachedMessageFormats
考虑到多数DefaultMessage都是一些同样错误消息,所以Spring使用一个HashMap—cachedMessageFormats缓存了DefaultMessage的MessageFormat。
2、StaticMessageSource
继承AbstractMessageSource类,提供了最简单的MessageSource的实现。提供了两个操作:
- addMessage()将消息编号和对应的MessageFormat保存于一个HashMap中。
- resolveCode()根据消息编号和Local从HashMap中取得对应的MessageFormat。
3、ResourceBundleMessageSource
继承AbstractMessageSource类,实现了BeanClassLoaderAware接口,提供了一种使用Resource Bundle的Message Source的实现。
可以指定一个或一组查找message的文件,可包含目录和Classpath。
resolveCodeWithoutArguments()覆盖了父类的对应方法,实现了直接从配置文件读取message并直接返回,不使用MessageFormat。
调用getResourceBundle()取得对应的ResourceBundle,然后通过取得的ResourceBundle取得对应的MessageFormat。
- ResourceBundleMessageSource中的缓存实现。
针对每一个Basename都缓存一个不同Local的HashMap。
针对每一个ResourceBundle都缓存一个Code的Map,其中缓存了一个Code的不同Local的MessageFormat。
4、ReloadableResourceBundleMessageSource
继承AbstractMessageSource类,实现了ResourceLoaderAware接口,提供了一种可以Reload的MessageSource的实现。
通过自己实现的PropertiesPersister和PropertiesHolder,跳过ResoureBundle的缓存机制,实现一个可以刷新的MessageSoruce。
四、Spring消息体系的使用
1.ApplicationContext
实现了MessageSource接口,将getMessage的调用转发给实际的MessageSource的实现。
2.MessageSourceAware
当Spring的IOC容器发现实现了此接口的实例,就会将ApplicationContext注入到此实例中。