相信很多小伙伴在用国际化配置文件来配置错误消息提示,但总是无法映射到自己配置的消息,楼主也折腾了一下午,因为网上的大量配置都是基于XML的,但楼主用的是基于Java的配置,其实原理是一样的,楼主走了不少弯路,接下来和大家分享下。
首先我们通过Spring的源代码来分析下Valid的实现流程,当前端控制器器发现控制器有@Valid这个 annotation时,会去找实现这个annotion的类,定义这个annotion的接口在org.springframwork.validation这个包里,在这个包里,定义这个annotation的是 Validator这个接口,另外spring还定义了一个SmartValidator这个接口,这个接口继承了原始的Valiatoto接口,我也不明白他为什么要这样做.我们可以看到, SpringValidatorAdapter这个类实现了这个接口,看名字,我们就知道它用了适配器的模式,所以整个大逻辑就在这个类里了,如果我们自定义了 validation类,那么Spring就会用我们自定义的bean,否则就用Spring自己的校验类,具体校验逻辑我们就不看,校验完后,结果会被交给DataBinder,这个类实现了Spring的Error接口,Error类定义了对应错误信息的处理机制 ,它根据errorCode去Valid Bean 中绑定的ResourceMessgae去找信息,这样Spring就会通过key来找到对应的信息。如果我们没有自定义Valid类时,Spring会用它自己的校验类,处理逻辑是相同,只不过它自己会从类的根目录下去找ValidationMessages.propertis,这个名字是它自己定义的,如果没有找到,他就会返回defaultMessage,也就是我在校验规则时写的错误信息。
好了,原理粗略的了解下,那么核心就是要让Spring使用我们自定义的ResourceMessage.刚开始我只是在RootConfig中定义了自己的校验类,并且配置了ResourceMessage.
@Bean public LocalValidatorFactoryBean localValidatorFactoryBean() { LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean(); localValidatorFactoryBean.setProviderClass(HibernateValidator.class); localValidatorFactoryBean.setValidationMessageSource(ResourceBundleMessageSource()); return localValidatorFactoryBean; } @Bean public ResourceBundleMessageSource ResourceBundleMessageSource() { ResourceBundleMessageSource resourceBundleMessageSource = new ResourceBundleMessageSource(); resourceBundleMessageSource.setBasename("i18n/ValidationMessages"); resourceBundleMessageSource.setCacheSeconds(60); resourceBundleMessageSource.setDefaultEncoding("UTF-8"); return resourceBundleMessageSource; }
可是无论我怎么配置都映射不到国际化信息,机智的我就想是不是Spring根本就没用我的类,于是我就在根目录下写了个properties文件,果然映射了,为什么他没有用我定义的类那,其实很简单,我只是定义这个类,并没有地方用啊。所以我们要让Spring去用这个类.SpringMvc部分的配置代码继承了 WebMvcConfigureAdapter 这个类,在这个类中我找了原因。
@Override public void extendMessageConverters(List<HttpMessageConverter<?>> converters) { } /** * {@inheritDoc} * <p>This implementation returns {@code null} */ @Override public Validator getValidator() { return null; } /** * {@inheritDoc} * <p>This implementation is empty. */ @Override public void configureContentNegotiation(ContentNegotiationConfigurer configurer) { } /** * {@inheritDoc} * <p>This implementation is empty. */ @Override public void configureAsyncSupport(AsyncSupportConfigurer configurer) { }
SpringMvc会通过 getValidator来获取自定义的类,而如果我们没有覆写这个方法的时候,它直接就返回null了,那它就用自己默认的校验类了,所以我们必须将自定义的校验Bean 返回给SpringMvc,问题终于解决了,其实这个问题还是自己对Spring基于java配置不太熟悉造成的,其实基于XML的配置也一样,虽然你定义了校验Bean,但还是要告知Spring的,也就是 <mvc:annotation-driven validator="xxxxx"> 这个配置,有了这个配置,springMvc才会用你自己定义的处理类。总结一句话就是,大家配置好校验Bean时,一定要让SpringMvc知道,因为Spring获取校验类是从SpringMVC中获取的,其实Valid本来就属于SpringMvc的,而我错误的理解认为在Spring中,只要定义,了校验Bean,Spring在初始化时,就自动替换了默认的校验类。好了,贴粗自己的基于Java配置SpringMvc部分代码(并不是Spring 和 Servlet的配置代码,只是针对SpringMVC的配置代码)。
@Configuration @EnableWebMvc @ComponentScan("org.gameloft.www") public class RestServiceConfiguration extends WebMvcConfigurerAdapter { private static Logger logger = LogManager.getLogger(RestServiceConfiguration.class); public RestServiceConfiguration() { logger.info("Init RestService "); } /** * 注册拦截器 * @param registry */ public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new RequestProcessingTimeInterceptor()); } @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable(); } @Override public Validator getValidator() { logger.info("Init Validator "); return localValidatorFactoryBean(); } @Bean public LocalValidatorFactoryBean localValidatorFactoryBean() { LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean(); localValidatorFactoryBean.setProviderClass(HibernateValidator.class); localValidatorFactoryBean.setValidationMessageSource(ResourceBundleMessageSource()); return localValidatorFactoryBean; } @Bean public ResourceBundleMessageSource ResourceBundleMessageSource() { ResourceBundleMessageSource resourceBundleMessageSource = new ResourceBundleMessageSource(); resourceBundleMessageSource.setBasename("i18n/ValidationMessages"); resourceBundleMessageSource.setCacheSeconds(60); resourceBundleMessageSource.setDefaultEncoding("UTF-8"); return resourceBundleMessageSource; } }