当前位置: 代码迷 >> 综合 >> SpringMVC(Spring4)——RESTful CRUD
  详细解决方案

SpringMVC(Spring4)——RESTful CRUD

热度:10   发布时间:2023-12-17 12:30:03.0

1.查询:

@RequestMapping("/emps")public String list(Map<String, Object> map){map.put("employees", employeeDao.getAll());return "list";}
<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<!--  SpringMVC 处理静态资源:1. 为什么会有这样的问题:优雅的 REST 风格的资源URL 不希望带 .html 或 .do 等后缀若将 DispatcherServlet 请求映射配置为 /, 则 Spring MVC 将捕获 WEB 容器的所有请求, 包括静态资源的请求, SpringMVC 会将他们当成一个普通请求处理, 因找不到对应处理器将导致错误。2. 解决: 在 SpringMVC 的配置文件中配置 <mvc:default-servlet-handler/>
-->
<script type="text/javascript" src="scripts/jquery-1.9.1.min.js"></script>
<script type="text/javascript">$(function(){$(".delete").click(function(){var href = $(this).attr("href");$("form").attr("action", href).submit();			return false;});})
</script>
</head>
<body><form action="" method="POST"><input type="hidden" name="_method" value="DELETE"/></form><c:if test="${empty requestScope.employees }">没有任何员工信息.</c:if><c:if test="${!empty requestScope.employees }"><table border="1" cellpadding="10" cellspacing="0"><tr><th>ID</th><th>LastName</th><th>Email</th><th>Gender</th><th>Department</th><th>Edit</th><th>Delete</th></tr><c:forEach items="${requestScope.employees }" var="emp"><tr><td>${emp.id }</td><td>${emp.lastName }</td><td>${emp.email }</td><td>${emp.gender == 0 ? 'Female' : 'Male' }</td><td>${emp.department.departmentName }</td><td><a href="emp/${emp.id}">Edit</a></td><td><a class="delete" href="emp/${emp.id}">Delete</a></td></tr></c:forEach></table></c:if><br><br><a href="emp">Add New Employee</a></body>
</html>

2.添加:

@RequestMapping(value="/emp", method=RequestMethod.GET)public String input(Map<String, Object> map){map.put("departments", departmentDao.getDepartments());map.put("employee", new Employee());return "input";}
<%@page import="java.util.HashMap"%>
<%@page import="java.util.Map"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body><form action="testConversionServiceConverer" method="POST"><!-- lastname-email-gender-department.id 例如: GG-gg@atguigu.com-0-105 -->Employee: <input type="text" name="employee"/><input type="submit" value="Submit"/></form><br><br><!--  1. WHY 使用 form 标签呢 ?可以更快速的开发出表单页面, 而且可以更方便的进行表单值的回显2. 注意:可以通过 modelAttribute 属性指定绑定的模型属性,若没有指定该属性,则默认从 request 域对象中读取 command 的表单 bean如果该属性值也不存在,则会发生错误。--><br><br><form:form action="${pageContext.request.contextPath }/emp" method="POST" modelAttribute="employee"><form:errors path="*"></form:errors><br><c:if test="${employee.id == null }"><!-- path 属性对应 html 表单标签的 name 属性值 -->LastName: <form:input path="lastName"/><form:errors path="lastName"></form:errors></c:if><c:if test="${employee.id != null }"><form:hidden path="id"/><input type="hidden" name="_method" value="PUT"/><%-- 对于 _method 不能使用 form:hidden 标签, 因为 modelAttribute 对应的 bean 中没有 _method 这个属性 --%><%-- <form:hidden path="_method" value="PUT"/>--%></c:if><br>Email: <form:input path="email"/><form:errors path="email"></form:errors><br><% Map<String, String> genders = new HashMap();genders.put("1", "Male");genders.put("0", "Female");request.setAttribute("genders", genders);%>Gender: <br><form:radiobuttons path="gender" items="${genders }" delimiter="<br>"/><br>Department: <form:select path="department.id" items="${departments }" itemLabel="departmentName" itemValue="id"></form:select><br><!--  1. 数据类型转换2. 数据类型格式化3. 数据校验. 1). 如何校验 ? 注解 ?①. 使用 JSR 303 验证标准②. 加入 hibernate validator 验证框架的 jar 包③. 在 SpringMVC 配置文件中添加 <mvc:annotation-driven />④. 需要在 bean 的属性上添加对应的注解⑤. 在目标方法 bean 类型的前面添加 @Valid 注解2). 验证出错转向到哪一个页面 ?注意: 需校验的 Bean 对象和其绑定结果对象或错误对象时成对出现的,它们之间不允许声明其他的入参3). 错误消息 ? 如何显示, 如何把错误消息进行国际化-->Birth: <form:input path="birth"/><form:errors path="birth"></form:errors><br>Salary: <form:input path="salary"/><br><input type="submit" value="Submit"/></form:form></body>
</html>
@RequestMapping(value="/emp", method=RequestMethod.POST)public String save(@Valid Employee employee, Errors result, Map<String, Object> map){System.out.println("save: " + employee);if(result.getErrorCount() > 0){System.out.println("???í??!");for(FieldError error:result.getFieldErrors()){System.out.println(error.getField() + ":" + error.getDefaultMessage());}map.put("departments", departmentDao.getDepartments());return "input";}employeeDao.save(employee);return "redirect:/emps";}

3.删除:

@RequestMapping(value="/emp/{id}", method=RequestMethod.DELETE)public String delete(@PathVariable("id") Integer id){employeeDao.delete(id);return "redirect:/emps";}

4.修改:

@ModelAttributepublic void getEmployee(@RequestParam(value="id",required=false) Integer id,Map<String, Object> map){if(id != null){map.put("employee", employeeDao.get(id));}}@RequestMapping(value="/emp", method=RequestMethod.PUT)public String update(Employee employee){employeeDao.save(employee);return "redirect:/emps";}@RequestMapping(value="/emp/{id}", method=RequestMethod.GET)public String input(@PathVariable("id") Integer id, Map<String, Object> map){map.put("employee", employeeDao.get(id));map.put("departments", departmentDao.getDepartments());return "input";}

5.处理静态资源:

优雅的REST风格的资源URL不希望带.html或.do等后缀。

若将DispatcherServlet请求映射配置为/,则SpringMVC将捕获WEB容器的所有请求,包括静态资源的请求。

可以在SpringMVC的配置文件中配置<mvc:default-servlet- handler/> 的方式解决静态资源的问题:

-<mvc:default-servlet-handler/> 将在SpringMVC上下文中定义一个DefaultServletHttpRequestHandler,它会进入DispatcherServlet的请求进行筛查,如果发现没有经过映射的请求就会将请求交给WEB应用服务器默认的Servlet处理。

-一般WEB应用服务器默认的Servlet的名称是default。若所使用的WEB服务器的默认Servlet名称不是default,则需要通过default- servlet-name 属性显示指定。

<mvc:default-servlet-handler/>

注意这个时候必须还要<mvc:annotation-driven></mvc:annotation-driven>不然就无法访问资源:

<mvc:annotation-driven></mvc:annotation-driven>

6.数据绑定流程:

SpringMVC主框架将ServletRequest对象及目标方法的入参实例传递给WebDataBinderFactory实例,以创建DataBinder实例对象。

DataBinder调用装配在SpringMVC上下文中的ConversionService组件进行数据类型的转换、数据格式化工作。将Servlet中的请求信息填充到入参对象中。

调用Validator组件对已经绑定了请求消息的入参对象进行数据合法性校验,并最终生成数据绑定结果BindingData对象。

SpringMVC抽取BindingResult中的入参对象和校验错误对象,将它们赋给处理方法的响应入参。

SpringMVC通过反射机制对目标处理方法进行解析,将请求消息绑定到处理方法的入参中。数据绑定的核心部件是DataBinder。运行机制:

7.数据类型转换:

7.1 SpringMVC内建的转换器:

ConversionService converters =
– java.lang.Boolean -> java.lang.String :
org.springframework.core.convert.support.ObjectToStringConverter@f874ca
– java.lang.Character -> java.lang.Number : CharacterToNumberFactory@f004c9
– java.lang.Character -> java.lang.String : ObjectToStringConverter@68a961
– java.lang.Enum -> java.lang.String : EnumToStringConverter@12f060a
– java.lang.Number -> java.lang.Character : NumberToCharacterConverter@1482ac5
– java.lang.Number -> java.lang.Number : NumberToNumberConverterFactory@126c6f
– java.lang.Number -> java.lang.String : ObjectToStringConverter@14888e8
– java.lang.String -> java.lang.Boolean : StringToBooleanConverter@1ca6626
– java.lang.String -> java.lang.Character : StringToCharacterConverter@1143800
– java.lang.String -> java.lang.Enum : StringToEnumConverterFactory@1bba86e
– java.lang.String -> java.lang.Number : StringToNumberConverterFactory@18d2c12
– java.lang.String -> java.util.Locale : StringToLocaleConverter@3598e1
– java.lang.String -> java.util.Properties : StringToPropertiesConverter@c90828
– java.lang.String -> java.util.UUID : StringToUUIDConverter@a42f23
– java.util.Locale -> java.lang.String : ObjectToStringConverter@c7e20a
– java.util.Properties -> java.lang.String : PropertiesToStringConverter@367a7f
– java.util.UUID -> java.lang.String : ObjectToStringConverter@112b07f ......

7.2 自定义类型转换器:

ConversionService是Spring类型转换的核心接口。可以利用ConversionServiceFactoryBean在Spring的IOC容器中定义一个ConversionService。

Spring将自动识别出IOC容器中的ConversionService,并在Bean属性配置及SpringMVC处理方法入参绑定等场合使用它进行数据等转换。

可通过ConversionServiceFactoryBean的converters属性注册自定义类型转换器。

Spring定义了3种类型的转换器接口,实现任意一种转换器接口都可以作为自定义转换器注册到ConversionServiceFactroyBean中:

Converter<S,T>:将S类型转换为T类型对象。

ConverterFactory:将相同系列多个“同质”Converter封装在一起。如果想一种类型的对象转换为另一种类型及其子类的对象(例如String转换为Number及Number的子类Integer、?Long、?Double等等)可以使用该转换器工厂类。

GenericConverter:会根据源类对象及目标类对象所在的宿主类中的上下文信息进行类型转换。

<!--会将自定义的ConversionService注册到SpringMVC的上下文-->
<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven><!-- 配置 ConversionService --><bean id="conversionService"class="org.springframework.format.support.FormattingConversionServiceFactoryBean"><property name="converters"><set><ref bean="employeeConverter"/></set></property>	</bean>
package com.atguigu.springmvc.converters;import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;import com.atguigu.springmvc.crud.entities.Department;
import com.atguigu.springmvc.crud.entities.Employee;@Component
public class EmployeeConverter implements Converter<String, Employee> {@Overridepublic Employee convert(String source) {if(source != null){String [] vals = source.split("-");//GG-gg@atguigu.com-0-105if(vals != null && vals.length == 4){String lastName = vals[0];String email = vals[1];Integer gender = Integer.parseInt(vals[2]);Department department = new Department();department.setId(Integer.parseInt(vals[3]));Employee employee = new Employee(null, lastName, email, gender, department);System.out.println(source + "--convert--" + employee);return employee;}}return null;}}
@RequestMapping("/testConversionServiceConverer")public String testConverter(@RequestParam("employee") Employee employee){System.out.println("save: " + employee);employeeDao.save(employee);return "redirect:/emps";}

8.关于mvc:annotation-driven:

<mvc:annotation-driven />会自动注册到RequestMappingHandlerMapping?、RequestMappingHandlerAdapter 与?ExceptionHandlerExceptionResolver三个Bean。

还提供以下支持:

-支持ConversionService实例对表单参数进行类型转换。

-支持使用@NumberFormat注解、?@DateTimeFormat注解完成数据类型格式化。

-支持使用@Valid注解对JavaBean实例进行JSR 303验证。

-支持使用@RequestBody和@ResponseBody注解。

9.@InitBinder:

由@InitBinder标识的方法,可以对WebDataBinder对象进行初始化。WebDataBinder是DataBinder的子类,用于完成表单数据到JavaBean属性的绑定。

@InitBinder方法不能没有返回值,它必须声明为void。

@InitBinder方法的参数通常是WebDataBinder。

@InitBinderpublic void initBinder(WebDataBinder binder){binder.setDisallowedFields("lastName");}

10.数据格式化:

Spring在格式化模块中定义了一个实现ConversionService接口的FormattingConversionService实现类,该实现类拓展了GenericConversionService,因此它具有类型转换的功能,又具有格式化功能。

FormattingConversionService拥有一个FormattingConversionServiceFactroyBean工厂类,后者用于在Spring上下文中构造前者。

FormattingConversionServiceFactroyBean内部已经注册了:

-NumberFormatAnnotationFormatterFactroy:支持对数字类型属性使用@NumberFormat注解。

-JodaDateTimeFormatAnnotationFormatterFactroy:支持对日期类型的属性使用@DateTimeFormat注解。

装配了FormattingConversionServiceFactroyBean之后,就可以在SpringMVC入参绑定及模型数据输出时使用注解驱动。

<mvc:annotation-driven/> 默认创建的?????ConversionService实例即为????FormattingConversionServiceFactroyBean。

10.1 日期格式化:

@DateTimeFormat注解可对????java.util.Date?、java.util.Calendar、?java.long.Long 时间类型进行标注:

-pattern??????????????属性:类型为字符串。指定解析/格式化字段数据的模式,如:/??????????? ??”yyyy-MM-dd hh:mm:ss”?? ???????。

-iso属性:类型为???DateTimeFormat.ISO?????。指定解析/格式化字段数据的ISO模式。包括四种:/??????? ?ISO.NONE?????(不使用)--默认、 ???ISO.DATE(yyyy-MM-dd)、 ?ISO.TIME(hh:mm:ss.SSSZ)?、ISO.DATE_TIME(yyyy-MM-dd hh:mm:ss.SSSZ)。

-style属性:字符串类型。通过样式指定日期时间的格式,由两位字符组成,第一位字符表示日期的格式,第二位表示时间的格式:S:短日期/时间格式、M:中。。。、L:长。。。、F:完整。。。、-:忽略。。。

10.2 数值格式化:

@NumberFormat可对类似数字类型的属性进行标注,它拥有两个互斥的属性:

-style:类型为NumberFormat.Style。用于指定样式类型,包括三种:Style.NUMBER(正常数字类型)、Style.CURRENCY(货币类型)、Style.PERCENT(百分数类型)。

-pattern:类型为String,自定义样式。如pattern="#,###";

@DateTimeFormat(pattern="yyyy-MM-dd")private Date birth;@NumberFormat(pattern="#,###,###.#")private Float salary;

11.数据校验:

JSR 303是Java为Bean的数据合法性校验提供的标准框架,它已经包含在了JavaEE 6.0中。

JSR 303通过在Bean属性上标注类似@NotNull、@Max等标准的注解指定校验规则,并通过标准的验证接口对Bean进行验证。

Hibernate Validator是JSR 303的一个参考实现,除了支持所有标准的校验注解外,它还支持:

需要的Jar包:

然后需要Spring容器中定义一个LocalValidatorFactoryBean;<mvc:annotation-driven/>会默认装配好一个LocalValidatorFactoryBean。

然后通过在处理方法的入参上标注@valid注解

@NotEmptyprivate String lastName;@Emailprivate String email;@Past@DateTimeFormat(pattern="yyyy-MM-dd")private Date birth;
@RequestMapping(value="/emp", method=RequestMethod.POST)public String save(@Valid Employee employee, Errors result, Map<String, Object> map){System.out.println("save: " + employee);if(result.getErrorCount() > 0){System.out.println("");for(FieldError error:result.getFieldErrors()){System.out.println(error.getField() + ":" + error.getDefaultMessage());}map.put("departments", departmentDao.getDepartments());return "input";}employeeDao.save(employee);return "redirect:/emps";}

Errors接口提供了获取错误信息的方法,如getErrorCount()、getFieldErrors(String field)。

BindingResult拓展了Errors接口。

需要验证的Bean需要和其绑定结果对象或错误对象成对出现。

常用方法:

– FieldError getFieldError(String field)

– List<FieldError> getFieldErrors()

– Object getFieldValue(String field)

– Int getErrorCount()

12.提示消息国际化:

每个属性在数据绑定和数据校验发生错误时,都会生成一个对应的FieldError对象。

当一个属性验证失败后,校验框架会为该属性生成4个消息代码,这些代码以校验注解名为前缀,结合modleAttribute、属性名及属性类型名生成多个对应的消息代码:例如User中的password标注了一个@Pattern注解,当password不满足要求时会产生4个错误代码:

– Pattern.user.password

– Pattern.password

– Pattern.java.lang.String

– Pattern

还有数据绑定时数据类型不匹配时的typeMismatch;必要参数不存在required;调用处理方法出错methodInvocation等等。

NotEmpty.employee.lastName=^^LastName\u4E0D\u80FD\u4E3A\u7A7A.
Email.employee.email=Email\u5730\u5740\u4E0D\u5408\u6CD5
Past.employee.birth=Birth\u4E0D\u80FD\u662F\u4E00\u4E2A\u5C06\u6765\u7684\u65F6\u95F4. typeMismatch.employee.birth=Birth\u4E0D\u662F\u4E00\u4E2A\u65E5\u671F. i18n.user=User
i18n.password=Password
NotEmpty.employee.lastName=^^LastName\u4E0D\u80FD\u4E3A\u7A7A.
Email.employee.email=Email\u5730\u5740\u4E0D\u5408\u6CD5
Past.employee.birth=Birth\u4E0D\u80FD\u662F\u4E00\u4E2A\u5C06\u6765\u7684\u65F6\u95F4. typeMismatch.employee.birth=Birth\u4E0D\u662F\u4E00\u4E2A\u65E5\u671F. i18n.user=\u7528\u6237\u540D
i18n.password=\u5BC6\u7801
NotEmpty.employee.lastName=^^LastName\u4E0D\u80FD\u4E3A\u7A7A.
Email.employee.email=Email\u5730\u5740\u4E0D\u5408\u6CD5
Past.employee.birth=Birth\u4E0D\u80FD\u662F\u4E00\u4E2A\u5C06\u6765\u7684\u65F6\u95F4. typeMismatch.employee.birth=Birth\u4E0D\u662F\u4E00\u4E2A\u65E5\u671F. i18n.user=User
i18n.password=Password

当使用SpringMVC标签显示错误消息的时候,SpringMVC会查看WEB上下文是否装配了国际化消息。

<!-- 配置国际化资源文件 --><bean id="messageSource"class="org.springframework.context.support.ResourceBundleMessageSource"><property name="basename" value="i18n"></property></bean>
  相关解决方案