基於Java配置Spring @Valid無法映射國際化得解決

    相信很多小夥伴在用國際化配置文件來配置錯誤消息提示,但總是無法映射到自己配置的消息,樓主也折騰了一下午,因爲網上的大量配置都是基於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;
    }
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章