运行环境jdk1.8,tomcat 8.5.13 , jetty9.4.6
开发框架spring mvc+spring 版本:3.2.17.RELEASE
Hibernate Validator 开始的版本是:5.4.0.Final
spring mvc的配置文件(只列出与此文有关的)
<mvc:annotation-driven />
<!-- bind your messages.properties -->
<bean class="org.springframework.context.support.ResourceBundleMessageSource" id="messageSource">
<property name="basename" value="messages"/>
<property name="defaultEncoding" value="UTF-8" />
</bean>
在控制器中注入MessageSource
@Autowired
private MessageSource messageSource;
请求方法中获得表单的校验错误
@RequestMapping(value="edit", method=RequestMethod.POST)
public String boardAction(
@ModelAttribute("form") @Valid BoardForm form,
BindingResult result,
SessionStatus status,
Model model,
UserSessionBean usb,
HttpServletRequest request,
HttpServletResponse response){
if(result.hasErrors()){
FieldError errorField=(FieldError)result.getAllErrors().get(0);
model.addAttribute("errors", messageSource.getMessage(errorField, null));
model.addAttribute("form", form);
return "forum/board/edit";
}
//ETC
注意,如果方法的参数是如下顺序:
public String boardAction(
@ModelAttribute("form") @Valid BoardForm form,
HttpServletRequest request,
HttpServletResponse response,
BindingResult result,
SessionStatus status,
Model model,
UserSessionBean usb){
将request和response提到了form之后,如果校验失败,会直接提示http 400 , 根本没有机会显示错误消息
注意 , 如果BoardForm 中的注解是自定义消息:
public class BoardForm extends ActionForm{
@NotEmpty(message="{NotEmpty.boardForm.name}")
private String name;
@SafeHtml(whitelistType=SafeHtml.WhiteListType.NONE,message="{SafeHtml.boardForm.description}")
private String description="";
private String image="defaultIco.png";
@NotEmpty(message="{NotEmpty.boardForm.status}")
private String status="1";
private long record=0L;
//SET/GET
}
在网页上只会显示:{NotEmpty.boardForm.name},不会显示NotEmpty.boardForm.name对应的消息内容,开始一直不解(答案后续说).但用单元测试是可以显示消息内容的而不是消息的模板:
public class RegisterFormTest {
private Validator validator;
@Before
public void setUp() {
//ValidatorFactory factory=Validation.buildDefaultValidatorFactory();
//validator = factory.getValidator();
validator = Validation
.byDefaultProvider()
.configure()
.messageInterpolator(new ResourceBundleMessageInterpolator(new PlatformResourceBundleLocator( "messages" )))
.buildValidatorFactory()
.getValidator();
}
//项目名/src/main/resources/ValidationMessages_zh_CN.properties
@Test
@Ignore
public void hello() {
PlatformResourceBundleLocator prbl=new PlatformResourceBundleLocator( "messages" );
ResourceBundle rb=prbl.getResourceBundle(Locale.CHINESE);
//资源文件测试
assertTrue(rb.containsKey("NotEmpty.ActionForm.token"));
}
@Test
public void test(){
RegisteForm form=new RegisteForm();
form.setCapture("123456");
form.setEmail("xiaofanku");
form.setUsername("xiaofanku");
form.setNew_password("123");
form.setPassword_confirm("123");
form.setToken("123456");
Set<ConstraintViolation<RegisteForm>> constraintViolations = validator.validate( form );
for(ConstraintViolation<RegisteForm> sv:constraintViolations){
//此处输出:{key}
System.out.println(sv.getMessageTemplate());
//此处输出:key对应的消息
System.out.println(sv.getMessage());
//sv.getPropertyPath()显示的是校验失败的属性名
}
}
}
至少现在跑在tomcat上没有什么异常出来, 实在不行用消息内嵌吗?jetty9.4.6就没哪么宽容了,项目起都起不来,异常如下:
2017-06-26 16:01:35 ERROR DispatcherServlet:470 - Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.validation.beanvalidation.LocalValidatorFactoryBean#0': Invocation of init method failed; nested exception is java.lang.NoClassDefFoundError: Could not initialize class org.hibernate.validator.internal.engine.ConfigurationImpl
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1514)
...
Caused by: java.lang.NoClassDefFoundError: Could not initialize class org.hibernate.validator.internal.engine.ConfigurationImpl
at org.hibernate.validator.HibernateValidator.createGenericConfiguration(HibernateValidator.java:33)
at javax.validation.Validation$GenericBootstrapImpl.configure(Validation.java:276)
at org.springframework.validation.beanvalidation.LocalValidatorFactoryBean.afterPropertiesSet(LocalValidatorFactoryBean.java:191)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1573)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1511)
... 53 more
java.lang.NoClassDefFoundError: Could not initialize class org.hibernate.validator.internal.engine.ConfigurationImpl,关于这个异常网上说什么的都有,我用maven管理项目不会出来多和少jar的问题?哪问题到底是spring?hibernate validator?jetty?
spring,hibernate validator,jetty都没问题
Spring Framework(3.2.17.RELEASE) Reference Documentation上的:Support for Hibernate 4.x;让我清醒了,是不是不支持hibernate validator 5.x呀.试试吗?
最后答案:
maven
<!-- bean validate-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.3.2.Final</version>
</dependency>
spring mvc配置
<!-- 不配此可能会输出消息模板-->
<mvc:annotation-driven validator="validator"/>
此处注意:
如果没有加:validator=”validator”,还是显示消息模板.
spring mvc配置
<!-- bind your messages.properties -->
<bean class="org.springframework.context.support.ResourceBundleMessageSource" id="messageSource">
<property name="basename" value="messages"/>
<property name="defaultEncoding" value="UTF-8" />
</bean>
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="providerClass" value="org.hibernate.validator.HibernateValidator"/>
<property name="validationMessageSource" ref="messageSource"/>
</bean>
如果有下面的异常:
PropertyAccessException 1: org.springframework.beans.MethodInvocationException: Property 'validationMessageSource' threw exception; nested exception is java.lang.NoClassDefFoundError: org/hibernate/validator/resourceloading/ResourceBundleLocator
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1455)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1160)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:519)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:458)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:293)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:290)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:191)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:328)
... 41 more
Caused by: org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessExceptions (1) are:
PropertyAccessException 1: org.springframework.beans.MethodInvocationException: Property 'validationMessageSource' threw exception; nested exception is java.lang.NoClassDefFoundError: org/hibernate/validator/resourceloading/ResourceBundleLocator
at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:101)
at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:57)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1452)
... 49 more
org.hibernate.validator.resourceloading.ResourceBundleLocator在4.3.2.Final中已经标记为:@Deprecated
package org.hibernate.validator.resourceloading;
import java.util.Locale;
import java.util.ResourceBundle;
@Deprecated
public interface ResourceBundleLocator extends org.hibernate.validator.spi.resourceloading.ResourceBundleLocator {
public ResourceBundle getResourceBundle(Locale locale);
}
所以没再试4.x的其它版本. 至此:tomcat8和jetty9上都可以正常运行.
另外说一下:
1. spring 3只支持JSR 303,spring 4才开始支持JSR-349,而hibernate validator 5.x才开始支持JSR-349
2. 不要在jdk8的环境下跑jetty9.2(jdk7,jetty9.3都可以跑在jdk8下了)我跑了一下出现以下异常:
2017-06-25 22:37:46,638 ERROR [Thread-0] disk.DiskStorageFactory (DiskStorageFactory.java:488) - Disk Write of -937817555943 failed:
java.lang.ClassCastException: sun.reflect.GeneratedSerializationConstructorAccessor2 cannot be cast to sun.reflect.SerializationConstructorAccessorImpl
at sun.reflect.MethodAccessorGenerator.generateSerializationConstructor(Unknown Source)
at sun.reflect.ReflectionFactory.generateConstructor(Unknown Source)
at sun.reflect.ReflectionFactory.newConstructorForSerialization(Unknown Source)
at java.io.ObjectStreamClass.getSerializableConstructor(Unknown Source)
at java.io.ObjectStreamClass.access$1500(Unknown Source)
at java.io.ObjectStreamClass$2.run(Unknown Source)
at java.io.ObjectStreamClass$2.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.io.ObjectStreamClass.<init>(Unknown Source)
at java.io.ObjectStreamClass.lookup(Unknown Source)
at java.io.ObjectOutputStream.writeObject0(Unknown Source)
at java.io.ObjectOutputStream.writeObject(Unknown Source)
at net.sf.ehcache.util.MemoryEfficientByteArrayOutputStream.serialize(MemoryEfficientByteArrayOutputStream.java:97)
at net.sf.ehcache.store.disk.DiskStorageFactory.serializeElement(DiskStorageFactory.java:403)
at net.sf.ehcache.store.disk.DiskStorageFactory.write(DiskStorageFactory.java:385)
at net.sf.ehcache.store.disk.DiskStorageFactory$DiskWriteTask.call(DiskStorageFactory.java:477)
at net.sf.ehcache.store.disk.DiskStorageFactory$PersistentDiskWriteTask.call(DiskStorageFactory.java:1071)
at net.sf.ehcache.store.disk.DiskStorageFactory$PersistentDiskWriteTask.call(DiskStorageFactory.java:1055)
at java.util.concurrent.FutureTask.run(Unknown Source)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(Unknown Source)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
sun.reflect.SerializationConstructorAccessorImpl在jdk8/rt.jar下,sun.reflect.GeneratedSerializationConstructorAccessor2这个就不知在哪了?
- jetty9.4 内置了不少模块,在我写的:Jetty9.2开始之路一文中要开启log4j,要复制三个jar包,在9.4中用
java -jar start.jar --add-to-start=logging-log4j
安装即可,也不需要找log4j.properties,安装后生成一个:log4j.xml.推荐大家用一下jetty.没有对比没有说服力:
tomcat8启动需要:1040ms
[org.springframework.web.servlet.DispatcherServlet] - Published WebApplicationContext of servlet 'bbs' as ServletContext attribute with name [org.springframework.web.servlet.FrameworkServlet.CONTEXT.bbs]
[org.springframework.web.servlet.DispatcherServlet] - FrameworkServlet 'bbs': initialization completed in 1040 ms
jetty9启动需要:703ms
[class org.springframework.web.servlet.mvc.ParameterizableViewController]
DispatcherServlet:476 - FrameworkServlet 'bbs': initialization completed in 703 ms