六. RESTful SpringMVC CRUD
注意:Handel需要添加@Controller
1. 显示信息
– URI:emps
– 请求方式:GET
jsp:
<a href="emps">List All Employees</a>
handle:
@RequestMapping("/emps")
public String list(Map<String,Object> map){
map.put("employees",employeeDao.getAll());
return "list";
}
通过RequestMapping结合视图跳转到list的jsp,将查询获得的信息存到map中,在jsp的中打印
list.jsp:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page isELIgnored="false" %>
<html>
<head>
<title>Title</title>
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.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.添加信息
– 显示添加页面:
• URI:emp
• 请求方式:GET
jsp:
<a href="emp">Add New Employee</a>
handle:
@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";
}
跳转到input.jsp填写表单,来获取添加的信息,获取的departments为表单中的需要的地址所有选项,employee用来绑定表单对应的对象。
3. 删除操作
– URL:emp/{id}
– 请求方式:DELETE
jsp:
<td><a class="delete" href="emp/${emp.id}">Delete</a></td>
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.js"></script>
<script type="text/javascript">
$(function () {
$(".delete").click(function () {
var href=$(this).attr("href");
$("form").attr("action",href).submit();
return false;
})
}
)
</script>
handle:
@RequestMapping(value = "/emp/{id}",method = RequestMethod.DELETE)
public String delete(@PathVariable("id") Integer id){
employeeDao.delete(id);
return "redirect:/emps";
}
delete方式只能由post提交方式返回,所以需要JavaScript来进行表单提交方式的修改,handle中拿到需要删除的id,删除后,重定向返回list.jsp
4. 修改操作:存在某个主键不可修改
– 显示修改页面:
• URI:emp/{id}
• 请求方式:GET
– 修改员工信息:
• URI:emp
• 请求方式:PUT
跳转到修改的表单提交页面
@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";
}
由前端传入的id从数据库得到对应对象传入map,然后跳转到input页面,input的选择标签会区分添加和修改,然后通过不同的表单提交方式转入不同的操作
handle中修改
@RequestMapping(value = "/emp",method = RequestMethod.PUT)
public String update(Employee employee){
employeeDao.change(employee);
return "redirect:/emps";
}
数据库修改以后,重定向跳转到表单
七.Spring 的表单标签和静态资源的处理
1.form标签
通过 SpringMVC 的表单标签可以实现将模型数据中的属性和 HTML 表单元素相绑定,以实现表单数据更便捷编辑和表单值的回显
为什么使用form标签
可以更快速的开发表单页面,而且可以更方便的进行表单值的回显 注意:
可以通过modelAttribute属性指定绑定的模型属性,若没有指定该属性,则默认从request域对象中读取command的表单bean
2.共有属性
SpringMVC 提供了多个表单组件标签,如 、 等,用以绑定表单字段的
属性值,它们的共有属性如下: – path:表单字段,对应 html 元素的 name 属性,支持级联属性 –
htmlEscape:是否对表单值的 HTML 特殊字符进行转换,默认值 为 true – cssClass:表单组件对应的 CSS
样式类名 – cssErrorClass:表单组件的数据存在错误时,采取的 CSS 样式
3.表单标签
• form:input、form:password、form:hidden、form:textarea :对应 HTML 表单的
text、password、hidden、textarea 标签 • form:radiobutton:单选框组件标签,当表单 bean
对应的 属性值和 value 值相等时,单选框被选中 • form:radiobuttons:单选框组标签,用于构造多个单选 框 –
items:可以是一个 List、String[] 或 Map – itemValue:指定 radio 的 value 值。可以是集合中
bean 的一个 属性值 – itemLabel:指定 radio 的 label 值 – delimiter:多个单选框可以通过
delimiter 指定分隔符 • form:checkbox:复选框组件。用于构造单个复选框 •
form:checkboxs:用于构造多个复选框。使用方式同 form:radiobuttons 标签 •
form:select:用于构造下拉框组件。使用方式同 form:radiobuttons 标签 •
form:option:下拉框选项组件标签。使用方式同 form:radiobuttons 标签 •
form:errors:显示表单组件或数据校验所对应的错误 –
4.处理静态资源
SpringMVC处理静态资源:
1.为什么会有这样的问题: 优雅的REST风格的资源URL不希望带.html或.do等后缀若将DispatcherServlet请求映射配置为/,则SpringMVC将捕获WEB容器的所有请求,包括静态的资源,SpringMVC会将他们当成一个普通请求处理,因找不到对应处理器将导致错误
2.解决: 在SpringMVC的配置文件中配置文件中配置
八.数据处理
1.数据类型转换
表单提取的字符串怎么变成我们需要的对象的?
Spring MVC 主框架将 ServletRequest 对象及目标方
法的入参实例传递给 WebDataBinderFactory 实例,以创
建 DataBinder 实例对象
• 2. DataBinder 调用装配在 Spring MVC 上下文中的
ConversionService 组件进行数据类型转换、数据格式
化工作。将 Servlet 中的请求信息填充到入参对象中
• 3. 调用 Validator 组件对已经绑定了请求消息的入参对象
进行数据合法性校验,并最终生成数据绑定结果
BindingData 对象
• 4. Spring MVC 抽取 BindingResult 中的入参对象和校验
错误对象,将它们赋给处理方法的响应入参
•5.关于mvc:annotation-driven
<mvc:annotation-driven /> 会自动注册RequestMappingHandlerMapping
、RequestMappingHandlerAdapter 与ExceptionHandlerExceptionResolver 三个bean。
• 还将提供以下支持:
– 支持使用 ConversionService 实例对表单参数进行类型转换
– 支持使用 @NumberFormat annotation、@DateTimeFormat注解完成数据类型的格式化
– 支持使用 @Valid 注解对 JavaBean 实例进行 JSR 303 验证
– 支持使用 @RequestBody 和 @ResponseBody 注解
2.数据类型格式化
• 对属性对象的输入/输出进行格式化,从其本质上讲依然
属于 “类型转换” 的范畴。
• Spring 在格式化模块中定义了一个实现
ConversionService 接口的
FormattingConversionService 实现类,该实现类扩展
了 GenericConversionService,因此它既具有类型转换的
功能,又具有格式化的功能
• FormattingConversionService 拥有一个
FormattingConversionServiceFactroyBean 工厂类,
后者用于在 Spring 上下文中构造前者
3.数据效验
(1).如何效验?使用注解?
①.使用JSR 303验证标准
②.加入hibernate validator验证框架的jar包或者maven依赖
③.在SpringMVC配置文件中添加 <mvc:annotation-driven/>
④.需要在bean的属性上添加对应的注解
⑤.需要在目标方法bean类型的前面添加@Valid注解
(2).验证出错转向到哪一个页面?
注意:需效验的Bean对象和其绑定结果对象或错误对象时成对出现的,他们之间不允许声明其他的入参
(3).错误消息?如果显示,如何把错误消息进行国际化
九.处理 JSON
步骤:
• 1. 加入 jar 包
• 2. 编写目标方法,使其返回 JSON 对应的对象或集合
• 3. 在方法上添加 @ResponseBody 注解
jsp:
<a href="testJson" id="testJson">Test Json</a>
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.js"></script>
<script type="text/javascript">
$(function(){
$("#testJson").click(
function () {
var url=this.href;
var args={};
$.post(url,args,function (data) {
for(var i=0;i<data.length;i++){
var id=data[i].id;
var lastName=data[i].lastName;
alert(id+": "+lastName)
}
})
return false;
}
)
})
</script>
handle:
@ResponseBody
@RequestMapping("/testJson")
public Collection<Employee> testJson(){
return employeeDao.getAll();
}
直接返回json对象给jsp进行alter打印
@RequestBody、@ResponseBody 示例
jsp:
<form action="testHttpMessageConverter" method="post" enctype="multipart/form-data">
File:<input type="file" name="file"/><br/>
Desc:<input type="text" name="desc"/><br/>
<input type="submit" value="Submit">
</form>
handle:
@ResponseBody
@RequestMapping("/testHttpMessageConverter")
public String testHttpMessageConverter(@RequestBody String body){
System.out.println(body);
return "hello world! "+new Date();
}
十.国际化
1.国际化概述
默认情况下,SpringMVC 根据 Accept-Language 参数 判断客户端的本地化类型。
当接受到请求时,SpringMVC 会在上下文中查找一个本 地化解析器(LocalResolver),找到后使用它获取请求所对应的本地化类型信息。
SpringMVC 还允许装配一个动态更改本地化类型的拦截 器,这样通过指定一个请求参数就可以控制单个请求的本地化类型。
关于国际化:
1.在页面上能够根据浏览器语言设置的情况对文本(不是内容),时间,数值进行本地化处理
2.可以在bean中获取国际化资源文件Locale对应的消息
3.可以通过超链接切换Locale,而不再依赖于浏览器的语言设置情况
解决:
1.使用JSTL的fmt标签
2.在bean中注入ResourceBundleMessageSource的示例,使用其对应的getMessage方法即可
3.配置LocalResolver和LocaleChangeInterceptor
<!--配置国际化资源文件-->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="i18n"/>
</bean>
<mvc:view-controller path="/i18n" view-name="i18n"/>
<mvc:view-controller path="/i18n2" view-name="i18n2"/>
十一.文件上传
Spring MVC 为文件上传提供了直接的支持,这种支持是通过即插即用的
MultipartResolver 实现的。Spring 用Jakarta Commons FileUpload
技术实现了一个MultipartResolver 实现类:CommonsMultipartResovler
Spring MVC 上下文中默认没有装配 MultipartResovler,因此默认情况下
不能处理文件的上传工作,如果想使用 Spring的文件上传功能,需现在上下文
中配置 MultipartResolver
1.配置 MultipartResolver
• defaultEncoding: 必须和用户 JSP 的 pageEncoding 属性
一致,以便正确解析表单的内容
• 为了让 CommonsMultipartResovler 正确工作,必须先
将 Jakarta Commons FileUpload 及 Jakarta Commons io
的类包添加到类路径下。
<!--配置commonsMultipartResolver-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="UTF-8"/>
<property name="maxInMemorySize" value="1024000"/>
</bean>
jsp:
<form action="testFileUpload" method="post" enctype="multipart/form-data">
File:<input type="file" name="file"/><br/>
Desc:<input type="text" name="desc"/><br/>
<input type="submit" value="Submit">
</form>
handle:
@RequestMapping("/testFileUpload")
public String testFileUpload(@RequestParam("desc") String desc, @RequestParam("file")MultipartFile file) throws IOException {
System.out.println("desc: "+desc);
System.out.println("originalFilename: "+file.getOriginalFilename());
System.out.println("inputStream: "+file.getInputStream());
return "success";
}
十二.拦截器
Spring MVC也可以使用拦截器对请求进行拦截处理,用户
可以自定义拦截器来实现特定的功能,自定义的拦截器必
须实现HandlerInterceptor接口
– preHandle():这个方法在业务处理器处理请求之前被调用,在该
方法中对用户请求 request 进行处理。如果程序员决定该拦截器对
请求进行拦截处理后还要调用其他的拦截器,或者是业务处理器去
进行处理,则返回true;如果程序员决定不需要再调用其他的组件
去处理请求,则返回false。
– postHandle():这个方法在业务处理器处理完请求后,但
是DispatcherServlet 向客户端返回响应前被调用,在该方法中对
用户请求request进行处理。
– afterCompletion():这个方法在 DispatcherServlet 完全处理完请
求后被调用,可以在该方法中进行一些资源清理的操作。
配置自定义拦截器
<mvc:interceptors>
<!--自定义拦截-->
<!--配置自定义的拦截器-->
<bean class="com.itlc.springMVC.interceptors.FirstInterceptor"/>
<!--配置拦截器作用(不作用)的路径-->
<mvc:interceptor>
<!--配置SecondInterceptor作用的路径-->
<mvc:mapping path="/emps"/>
<!--配置SecondInterceptor不作用的路径-->
<mvc:exclude-mapping path="/emp"/>
<bean class="com.itlc.springMVC.interceptors.SecondInterceptor"/>
</mvc:interceptor>
<!--配置LocaleChangeInterceptor-->
<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"/>
</mvc:interceptors>
自定义类
public class FirstInterceptor implements HandlerInterceptor{
/*
该方法在目标方法之前被调用。
若返回值为true,则继续调用后续的拦截器和目标方法
若返回值为false,则不会再调用后续的拦截器和目标方法
可以考虑做权限,日志,事务等。
*/
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
System.out.println("1preHandle");
return true;
}
/*
调用目标方法之后,但渲染视图之前
可以对请求域中的属性或视图做出修改
*/
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
System.out.println("1postHandle");
}
/*
渲染视图之后调用
释放资源
*/
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
System.out.println("1afterCompletion");
}
}
十三.异常处理
Spring MVC 通过 HandlerExceptionResolver 处理程序
的异常,包括 Handler 映射、数据绑定以及目标方法执行
时发生的异常。
SpringMVC 提供的 HandlerExceptionResolver 的实现类
jsp:
<a href="testExceptionHandlerExceptionResolver?i=0">Test ExceptionHandlerExceptionResolver</a>
handle:
@ExceptionHandler({ArithmeticException.class})
public ModelAndView handleArithmeticException(Exception ex){
System.out.println("Arithmetic异常:" + ex);
ModelAndView mv=new ModelAndView("error");
mv.addObject("exception",ex);
return mv;
}
@ExceptionHandler({RuntimeException.class})
public ModelAndView handleArithmeticException2(Exception ex){
System.out.println("Runtime异常:" + ex);
ModelAndView mv=new ModelAndView("error");
mv.addObject("exception",ex);
return mv;
}
@RequestMapping("/testExceptionHandlerExceptionResolver")
public String testExceptionHandlerExceptionResolver(@RequestParam("i") Integer i){
System.out.println(10/i);
return "success";
}
若无@ExceptionHandler配置,并标签内部参数对应异常的方法,浏览器报404;若有,则跳到这个方法中对异常进行处理。
1.在@ExceptionHandler方法的入参中可以加入Exception类型的参数,该参数即对应发生的异常对象
2.@ExceptionHandler方法的入参中不能传入Map,若希望把异常信息传到页面上,需要使用
ModelAndView作为返回值
3.@ExceptionHandler方法标记的异常有优先级的问题,在当前Handle中匹配程度最高的
ExceptionHandler优先
4.@ControllerAdvice:如果在当前Handle中找不到@ExceptionHandler方法来处理当前方法产生的异常,则会去@ControllerAdvice标记的类中查找@ExceptionHandler标记的方法来处理异常
2.ResponseStatusExceptionResolver
• 在异常及异常父类中找到 @ResponseStatus 注解,然后使用这个注解的属性进行处理。
• 定义一个 @ResponseStatus 注解修饰的异常类
//加入@ResponseStatus可以设置状态码的错误信息
@ResponseStatus(value = HttpStatus.FORBIDDEN , reason = "lalalal")
public class MyException extends RuntimeException {
private static final long serialVersionUID=1L;
}
若在处理器方法中抛出了上述异常:若ExceptionHandlerExceptionResolver 不解析述异常。由于触发的异常 UnauthorizedException 带有@ResponseStatus注解。因此会被ResponseStatusExceptionResolver 解析到。最后响应HttpStatus.UNAUTHORIZED 代码给客户端。HttpStatus.UNAUTHORIZED 代表响应码401,无权限。
3.SimpleMappingExceptionResolver
• 如果希望对所有异常进行统一处理,可以使用SimpleMappingExceptionResolver,它将异常类名映射为视图名,即发生异常时使用对应的视图报告异常。
十四.在Spring环境下使用SpringMVC
需要Spring整合SpringMVC吗?
还是否需要再加入Spring的IOC容器?
是否需要web.xml文件中配置启动Spring IOC容器的ContextLoaderListener?需要:通常情况下,类似于数据源,事务,整合其他框架都是放在Spring的配置文件中的, 而不是放在SpringMVC的配置文件中,实际上放入Spring配置文件对应的IOC容器中的还有 Service和Dao。 不需要:都放在SpringMVC的配置文件中也可以,也可以分多个Spring的配置文件,然后使 用import节点导入其他的配置文件
问题:若Spring的IOC容器和SpringMVC的IOC容器扫描的包有重合的部分,就会导致有的bean
会被创建两次。
解决方法:
1.使Spring的IOC容器和使SpringMVC的IOC容器扫描的包没有重合的部分
2.使用exclude-filter和include-filter子节点来规定只能扫描的注解
选择第二种方法。
<!--配置扫描器-->
<context:component-scan base-package="com.itlc.springMVC3">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
</context:component-scan>
注意:SpringMVC的IOC容器中的bean来引用Spring IOC容器中的bean
反过来了? 不行,Spring的IOC容器中的bean不能来引用SpringMVC IOC容器中的bean
只有控制层能引用业务层,业务层不能引用控制层