How can I use a custom message from my .properties file but using the parameters provided by the @Size annotation?

It should be a simple problem, but I couldn't find a direct solution.

I am using a Spring messaging resource to provide all texts on my system from a .properties file. I need this for internationalization questions and cannot use the default messages from Spring.

Here's the problem:

I have the following line in my .properties file:

validator_invalid_state_name=Invalid name ({min}, {max})

      

Security Code:

@Size( min=3, max=5, message="validator_invalid_state_name" )
public String name;

      

However, when an invalid value is sent to the field, this message is:

"Invalid name ({min}, {max})" , not "Invalid name (3, 5)" .

I have tried several solutions without success, for example:

@Size( min=3, max=5, message="{validator_invalid_state_name}" )
@Size( min=3, max=5, message="${validator_invalid_state_name}" )
@Size( min=3, max=5, message="Size.name" ) // with "Size.name=Invalid name ({min}, {max})" in my .properties file
@Size( min=3, max=5, message="{Size.name}" ) //idem

      

But if I only use

@Size( min=3, max=5 )

      

Then I get the default message from Spring (which I don't want to use). It's very easy for me to use a custom post from my custom .properties file, but using the parameters provided by the @Size annotation.

Any suggestions?

Thanks in advance.

+3


source to share


2 answers


The correct syntax for key access would be

@Size( min=3, max=5, message="{validator_invalid_state_name}" )

      

The problem, however, is that in the default setting, the lookup for keys will not be done against your custom.properties and not against Spring ValidationMessages.properties. So the fastest way to get it up and running is to create ValidationMessages.properties (and 18n equivalents) and add your keys eg.

validator_invalid_state_name=Invalid name ({min}, {max})

      

This is the simplest solution and no configuration headaches.

Now, since you would like this to be done from your properties file, well, its feasibility, but you need to find the correct configuration. For Spring 4.x this will be the correct xml and java config.



Java configuration

@Bean(name = "messageSource")
public MessageSource messageSource()
{
    ReloadableResourceBundleMessageSource bean = new ReloadableResourceBundleMessageSource();
    bean.setBasename("classpath:text");
    bean.setDefaultEncoding("UTF-8");
    return bean;
}

@Bean(name = "validator")
public LocalValidatorFactoryBean validator()
{
    LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
    bean.setValidationMessageSource(messageSource());
    return bean;
}

@Override
public Validator getValidator()
{
    return validator();
}

      

XML configuration

    <annotation-driven validator="validator">
    <!--i18n filtering-->
    <beans:bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
        <beans:property name="basenames">
            <beans:list>
                <beans:value>classpath:custom</beans:value>
            </beans:list>
        </beans:property>
        <beans:property name="defaultEncoding" value="UTF-8"/>
    </beans:bean>

  <beans:bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
        <beans:property name="validationMessageSource" ref="messageSource"/>
    </beans:bean>

      

For Spring mvc 3.x you will not find validationMessageSource property on LocalValidatorFactoryBean, for that you will need to create your message interpolator and configure it. You have an example in this thread http://forum.spring.io/forum/spring-projects/web/78675-jsr-303-validation-spring-mvc-and-messages-properties . Hope it helps.

+5


source


This solution doesn't work if you are using Spring Boot with JPA persistence support. The system throws out:

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#427e977': Cannot resolve reference to bean 'entityManagerFactory' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory': Post-processing of FactoryBean singleton object failed; nested exception is java.lang.IllegalStateException: ApplicationEventMulticaster not initialized - call 'refresh' before multicasting events via the context: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@4f430ea5: startup date [Fri Sep 18 09:33:54 EEST 2015]; root of context hierarchy
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:359)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:108)
at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:634)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:444)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1119)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1014)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:504)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveInnerBean(BeanDefinitionValueResolver.java:299)
... 96 more

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory': Post-processing of FactoryBean singleton object failed; nested exception is java.lang.IllegalStateException: ApplicationEventMulticaster not initialized - call 'refresh' before multicasting events via the context: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@4f430ea5: startup date [Fri Sep 18 09:33:54 EEST 2015]; root of context hierarchy
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:116)
at org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance(AbstractBeanFactory.java:1523)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:314)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:351)
... 104 more
Caused by: java.lang.IllegalStateException: ApplicationEventMulticaster not initialized - call 'refresh' before multicasting events via the context: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@4f430ea5: startup date [Fri Sep 18 09:33:54 EEST 2015]; root of context hierarchy
at org.springframework.context.support.AbstractApplicationContext.getApplicationEventMulticaster(AbstractApplicationContext.java:344)
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:331)
at org.springframework.boot.autoconfigure.orm.jpa.DataSourceInitializedPublisher.postProcessAfterInitialization(DataSourceInitializedPublisher.java:69)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:422)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.postProcessObjectFromFactoryBean(AbstractAutowireCapableBeanFactory.java:1719)
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:113)
... 108 more

      



(I think what is happening, anyway, is that we lose sight of the application.properties where the spring.datasource statements are located, but I have no idea why!). I was able to get this to work with the following java config by dropping the custom messageSource definition as mentioned in the master slave above and inserting the SpringBoot supplied messageSource. This one now populates my validation messages from messages.properties instead of ValidationMessages.properties

@SpringBootApplication
public class MyApplication extends WebMvcConfigurerAdapter {

@Autowired
MessageSource messageSource;

public static void main(String[] args) {
    SpringApplication.run(MyApplication.class, args);
}

    @Bean(name = "validator")
public LocalValidatorFactoryBean validator()
{
    LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
    bean.setValidationMessageSource(messageSource);
    return bean;
}

@Override
public Validator getValidator()
{
    return validator();
}
}

      

0


source







All Articles