这篇文章介绍如何使用SpringMVC实现简单的、REST风格的员工信息的增删改查操作。项目源码附在我的github中。
需求
① 查看所有员工信息:(url:emps,method:GET)
② 添加员工信息,添加后重定向到显示员工信息页面:(url:emp,method:POST)
③ 更新员工信息。LastName字段不可修改,要求能够回显表单,更新后重定向到显示员工信息页面:(url:emp/{id},method:PUT)
④ 删除员工信息,删除后重定向到显示员工信息页面。(url:emp/{id},method:DELETE)
实现
Employee.java:
Department.java:
EmployeeDao.java: (其中关于数据库的操作仅采用模拟方式实现)
DepartmentDao.java: (其中关于数据库的操作仅采用模拟方式实现)
控制器EmployeeHandler.java:
index.jsp:
WEB-INF/views/input.jsp:
WEB-INF/views/list.jsp
web.xml:
springmvc.xml:
lib目录:
核心知识点
1、SpringMVC的form标签:
1.1 通过SpringMVC的form标签可以实现将模型数据中的属性和HTML 表单元素相绑定,以实现表单数据更便捷编辑和表单值的回显。
1.2 可以通过SpringMVC的form标签的modelAttribute属性指定绑定的模型属性以进行表单回显。
1.3 SpringMVC的form标签默认一定要回显表单,若没有指定modelAttribute属性,则默认从request 域对象中读取command的表单bean。如果在 指定的modelAttribute 或者 command 中无法获取到某一属性,则会发生错误。
2、处理静态资源:
2.1 优雅的REST 风格的资源URL 不希望带.html 或.do 等后缀,若将DispatcherServlet请求映射配置为/,则Spring MVC 将捕获WEB 容器的所有请求,包括静态资源的请求(例如使用jQuery),SpringMVC会将他们当成一个普通请求处理,因找不到对应处理器将导致错误。
2.2 上述问题的解决方法为:在SpringMVC的配置文件中添加配置:
其原理是:default-servlet-handler 将在 SpringMVC 上下文中定义一个 DefaultServletHttpRequestHandler,它会对进入 DispatcherServlet 的请求进行筛查,如果发现是没有经过映射的请求, 就将该请求交由 WEB 应用服务器默认的 Servlet 处理。如果不是静态资源的请求,才由 DispatcherServlet 继续处理。一般情况下,WEB 应用服务器默认的 Servlet 的名称都是 default。若所使用的 WEB 服务器的默认 Servlet 名称不是 default,则需要通过 default-servlet-name 属性显式指定。
3.数据转换&数据格式化&数据校验
3.1 数据绑定流程:
① Spring MVC主框架将ServletRequest对象及目标方法的入参实例传递给WebDataBinderFactory实例,以创建DataBinder实例对象;
② DataBinder调用装配在Spring MVC上下文中的ConversionService组件进行数据类型转换、数据格式化工作。将Servlet 中的请求信息填充到入参对象中;
③ 调用Validator组件对已经绑定了请求消息的入参对象进行数据合法性校验,并最终生成数据绑定结果BindingData对象;
④ Spring MVC抽取BindingResult中的入参对象和校验错误对象,将它们赋给处理方法的响应入参
Spring MVC通过反射机制对目标处理方法进行解析,将请求消息绑定到处理方法的入参中。数据绑定的核心部件是DataBinder,运行机制如下:
实现源码:
3.2 数据转换
Spring MVC 上下文中内建了很多转换器,可完成大多数Java 类型的转换工作,如下图:
如果内建的转换器不能满足要求,可以自定义转换器。方法是利用 ConversionServiceFactoryBean 在Spring 的IOC 容器中定义一个 ConversionService,并通过ConversionServiceFactoryBean的 converters 属性注册自定义的类型转换器。Spring 将自动识别出IOC 容器中的ConversionService,并在Bean 属性配置及Spring MVC 处理方法入参绑定等场合使用它进行数据的转换。例如现在要实现一个将String转为Employee对象的转换器:(这里的Employee没有添加birth属性)
springmvc.xml:
jsp:
controller:
Converter:
3.3 数据格式化
对属性对象的输入/输出进行格式化,从其本质上讲依然属于“类型转换”的范畴。Spring 在格式化模块中定义了一个实现ConversionService接口的FormattingConversionService实现类,该实现类扩展了GenericConversionService,因此它既具有类型转换的功能,又具有格式化的功能。FormattingConversionService拥有一个FormattingConversionServiceFactroyBean工厂类,后者用于在Spring 上下文中构造前者。
FormattingConversionServiceFactroyBean内部已经注册了:
NumberFormatAnnotationFormatterFactroy:支持对数字类型的属性使用@NumberFormat注解
JodaDateTimeFormatAnnotationFormatterFactroy:支持对日期类型的属性使用@DateTimeFormat注解
装配了FormattingConversionServiceFactroyBean后,就可以在Spring MVC 入参绑定及模型数据输出时使用注解驱动了。mvc:annotation-driven标签 默认创建的ConversionService 实例即为 FormattingConversionServiceFactroyBean。
例如,我们在Employee.java中的birth属性上添加了注解:@DateTimeFormat(pattern=”yyyy-MM-dd”),即指定了日期输入的格式。此外,还可通过@NumberFormat注解对类似数字类型的属性进行格式化标注,例如
3.4 数据校验
JSR 303是Java 为Bean 数据合法性校验提供的标准框架,它已经包含在JavaEE 6.0中。JSR 303通过在Bean 属性上标注类似于@NotNull、@Max等标准的注解指定校验规则,并通过标准的验证接口对Bean 进行验证。详细注解如下图:
hibernate Validator是JSR 303 的一个参考实现,除支持所有标准的校验注解外,它还支持以下的扩展注解:
Spring MVC 数据校验:
Spring 4.0拥有自己独立的数据校验框架,同时支持JSR 303标准的校验框架。
Spring 在进行数据绑定时,可同时调用校验框架完成数据校验工作。在Spring MVC 中,可直接通过注解驱动的方式进行数据校验。
Spring 的LocalValidatorFactroyBean既实现了Spring 的Validator 接口,也实现了JSR 303 的Validator接口。只要在Spring 容器中定义了一个LocalValidatorFactoryBean,即可将其注入到需要数据校验的Bean 中。mvc:annotation-driven标签 会默认装配好一个LocalValidatorFactoryBean,通过在处理方法的入参上标注@valid注解即可让Spring MVC 在完成数据绑定后执行数据校验的工作。
Spring 本身并没有提供JSR303 的实现,所以必须将JSR303 的实现者的jar 包放到类路径下:
Spring MVC 是通过对处理方法签名的规约来保存校验结果的:前一个表单/命令对象的校验结果保存到随后的入参中,这个保存校验结果的入参必须是BindingResult或Errors类型,这两个类都位于org.springframework.validation包中。需校验的Bean 对象和其绑定结果对象或错误对象时成对出现的,它们之间不允许声明其他的入参。
例如,在上述的EmployeeHandler类的save方法中即声明了需要对employee入参进行校验,并将校验结果存入result中:
其中对employee的哪些字段进行何种校验,Employee.java中已经标注了,例如
标注了lastName字段不能为null。
在目标方法中获取校验结果:
上面save方法的代码已经介绍了如何利用校验结果的信息在目标方法中进行打印。
在页面上显示错误:
Spring MVC 除了会将表单/命令对象的校验结果保存到对应的BindingResult或Errors 对象中外,还会将所有校验结果保存到“隐含模型”,即implicitModel。隐含模型中的所有数据最终将通过HttpServletRequest的属性列表暴露给JSP 视图对象,因此在JSP 中可以获取错误信息。例如,如果上述的lastName字段为null,则可以在跳转的页面input.jsp中通过如下代码获取错误信息。
提示消息的国际化:
每个属性在数据绑定和数据校验发生错误时,都会生成一个对应的FieldError对象。当一个属性校验失败后,校验框架会为该属性生成4 个消息代码,这些代码以校验注解类名为前缀,结合modleAttribute、属性名及属性类型名生成多个对应的消息代码:例如User 类中的password 属性标准了一个@Pattern 注解,当该属性值不满足@Pattern 所定义的规则时, 就会产生以下4 个错误代码:
- Pattern.user.password
- Pattern.password
- Pattern.java.lang.String
- Pattern
当使用Spring MVC 标签显示错误消息时,Spring MVC 会查看WEB 上下文是否装配了对应的国际化消息,如果没有,则显示默认的错误消息,否则使用国际化消息。例如,我们要定制lastName字段的验证出错的错误消息:
创建国际化资源文件 i18n.properties:(其中“LastName\u4E0D\u80FD\u4E3A\u7A7A”是输入汉字“LastName不能为空”自动转换为ISO-8859-1编码的结果)
在springmvc.xml中配置国际化资源文件
那么如果lastName字段为null,错误信息将会是:LastName不能为空。
若数据类型转换或数据格式转换时发生错误,或该有的参数不存在,或调用处理方法时发生错误,都会在隐含模型中创建错误消息。其错误代码前缀说明如下:
- required:必要的参数不存在。如@RequiredParam(“param1”) 标注了一个入参,但是该参数不存在。
- typeMismatch:在数据绑定时,发生数据类型不匹配的问题
- methodInvocation:Spring MVC。 在调用处理方法时发生了错误–注册国际化资源文件。
4、关于mvc:annotation-driven
mvc:annotation-driven 会自动注册RequestMappingHandlerMapping、RequestMappingHandlerAdapter与ExceptionHandlerExceptionResolver三个bean。还将提供以下支持:
- 支持使用ConversionService实例对表单参数进行类型转换(不加的话就没有ConversionService)
- 支持使用@NumberFormat、@DateTimeFormat注解完成数据类型的格式化
- 支持使用@Valid注解对JavaBean 实例进行JSR 303验证
- 支持使用@RequestBody和@ResponseBody注解
5、关于@InitBinder注解
由@InitBinder标识的方法,可以对WebDataBinder对象进行初始化。WebDataBinder是DataBinder的子类,用于完成由表单字段到JavaBean 属性的绑定。
@InitBinder方法不能有返回值,它必须声明为void。
@InitBinder方法的参数通常是WebDataBinder。
例如,如下代码声明了不将请求参数中的lastName属性绑定到WebDataBinder中: