一、高级参数绑定
1.1 数组类型的参数绑定
基于批量删除商品操作完成参数传递。
jsp修改:
添加可选框,并设置name为ids,值为${item.id}
控制器修改
@RequestMapping("queryItem")
public String queryItem(QueryVo vo,Model model){
if(vo.getItem()!=null){
System.out.println(vo.getItem());
}
if(ids !=null &&ids.length>0){
for(Integer id:ids){
System.out.println(id);
}
}
return "itemList";
}
总结:在页面选中1、2商品时,并提交请求,会在控制台打印1、2,然后进行下一项尝试。
POJO修改
控制器修改
@RequestMapping("queryItem")
public String queryItem(QueryVo vo,Integer[] ids,Model model){
if(vo.getIds() !=null &&vo.getIds().length>0){
for(Integer id:vo.getIds()){
System.out.println(id);
}
}
if(ids !=null &&ids.length>0){
for(Integer id:ids){
System.out.println(id);
}
}
return "itemList";
}
小结:同样的请求操作,在queryvo中ids数组和形式参数接收数组ids的打印值相同,这就说明的springmvc的一个功能,可以自动的将页面的属性与接收参数进行匹配。
1.2 集合类型的参数绑定
修改pojo
修改jsp
<script type="text/javascript"
src="${pageContext.request.contextPath }/My97Date/WdatePicker.js"></script>
<form action="${pageContext.request.contextPath }/queryItem.action"
method="post">
查询条件:
<table width="100%" border=1>
<tr>
<td>商品名称:<input type="text" name="item.name" /></td>
<td>商品价格:<input type="text" name="item.price" /></td>
</tr>
<tr>
<td><input type="submit" value="查询" /></td>
</tr>
</table>
商品列表:
<table width="100%" border=1>
<tr>
<td>选择</td>
<td>商品名称</td>
<td>商品价格</td>
<td>生产日期</td>
<td>商品图片</td>
<td>商品描述</td>
<td>操作</td>
</tr>
<c:forEach items="${itemList }" var="item" varStatus="status">
<tr>
<td><input type="checkbox" name="ids" value="${item.id}" /></td>
<td>
<input type="hidden" name="items[${ status.index}].id"
value="${item.id }" />
<input type="text" name="items[${ status.index}].name"
value="${item.name }" />
</td>
<td>
<input type="text" name="items[${ status.index}].price"
value="${item.price }" />
</td>
<td>
<%-- <fmt:formatDate value="${item.createtime}" pattern="yyyy-MM-dd HH:mm:ss" /> --%>
<input readonly="readonly" type="text" name="items[${ status.index}].createtime"
value='<fmt:formatDate value="${item.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/>'
onclick="WdatePicker({dateFmt:'yyyy-MM-dd HH:mm:ss'})" />
</td>
<td>
<c:if test="${item.pic !=null}">
<img src="/pic/${item.pic}" width=100 height=100 />
<input type="hidden" name="items[${ status.index}].pic"
value="${item.pic }" />
<br />
</c:if>
</td>
<td>
<input type="text" name="items[${ status.index}].detail"
value="${item.detail }" />
</td>
<td>
<a href="${pageContext.request.contextPath }/itemEdit.action?
id=${item.id}">修改</a>
</td>
</tr>
</c:forEach>
</table>
</form>
添加My97Date日期控件
varStatus属性常用参数总结如下:
小结:这里还不足以实现批量修改的功能,因为可能会报一个日期错误(Http Status 400-)。解决办法:定义日期转换器并配置。
定义日期转换器:
package springmvc.utils;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.springframework.core.convert.converter.Converter;
/**
* 日期转换器
*
* Converter<S,T>
* S:Source 要转换的源类型
* T:目标,要转换成的数据类型
* @author Administrator
*
*/
public class DateConvert implements Converter<String, Date>{
@Override
public Date convert(String source) {
Date result=null;
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
result = sdf.parse(source);
} catch (ParseException e) {
e.printStackTrace();
}
return result;
}
}
在springmvc.xml中配置日期转换器
<!-- 配置注解驱动,相当于同时使用最新处理器映射跟处理器适配器,对json数据响应提供支持 -->
<!--使用自定义转换器 -->
<mvc:annotation-driven conversion-service="Myconvert" />
<!--定义转换器 -->
<bean id="Myconvert"
class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="springmvc.utils.DateConvert" />
</set>
</property>
</bean>
二、@RequestMapping注解的使用
2.1 路径映射可以是数组
2.2 @RequestMapping可以加在类头部,用于目录分级管理
2.3 限定请求方法
注意:当设置为get方式提交的时候,会出现乱码问题,解决办法:在tomcat的server.xml中,设置8080端口的地方增加:URIEncoding=“utf-8”
<Connector URIEncoding="utf-8" connectionTimeout="20000" port="8080"
protocol="HTTP/1.1" redirectPort="8443"/>
三、Controller方法返回值
3.1 返回ModelAndView
参考第一天内容
3.2 返回void
3.2.1 request
@RequestMapping("queryVoid")
public void queryVoid(HttpServletRequest request,HttpServletResponse response)
throws Exception{
//request响应客户请求
request.setAttribute("msg", "这个是request响应的消息");
request.getRequestDispatcher("/jsp/msg.jsp").forward(request, response);
}
3.2.2 response
@RequestMapping("queryVoid")
public void queryVoid(HttpServletRequest request,HttpServletResponse response)
throws Exception{
//response响应用户请求
response.sendRedirect("/itemList.action");
}
3.2.3 设置响应字符的编码
@RequestMapping("queryVoid")
public void queryVoid(HttpServletRequest request,HttpServletResponse response)
throws Exception{
//设置响应字符的编码(依然出现乱码:解决办法,将设置编码放到writer前面)
response.setContentType("text/html; charset=UTF-8");
// response.setCharacterEncoding("utf-8");
PrintWriter printWriter=response.getWriter();
printWriter.println("这个是response打印的消息");
}
3.3 返回String
3.3.1 返回视图名字,参考第一次笔记
3.3.2 redirect与forward
注:使用转发和重定向分别在页面中打印msg的值如何编写jsp语句
转发:
model.addAttribute("msg", "修改商品信息成功");
//跳转到商品列表页面
return "forward:itemList.action";
jsp页面:
<span>${ msg }</span><!--转发 -->
重定向
model.addAttribute("msg", "修改商品信息成功");
//跳转到商品列表页面
return "redirect:itemList.action";
使用重定向的时候msg信息存放在url中
jsp页面:
<span>${ param.msg }</span><!--重定向 -->
四、Springmvc中异常处理
思想:做一个全局异常处理器,处理所有没有处理过的运行时异常用于更友好地提示用户。
4.1 步骤
4.1.1 创建全局异常处理器
/**
* 全局异常处理器
* @author Steven
*
*/
public class CustomerException implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response, Object hanlder,
Exception e) {
//记录日志
e.printStackTrace();
//错误消息
String msg = "很抱歉,系统发生异常了,请联系管理员";
//响应用户错误提示
ModelAndView mav = new ModelAndView();
//返回错误消息
mav.addObject("msg", msg);
//响应错误提示页面
mav.setViewName("msg");
return mav;
}
}
4.1.2 配置异常处理器
<!--配置全局异常处理器 -->
<bean class="springmvc.utils.CustomerException" />
4.1.3 测试访问queryVoid.action
4.1.4 更智能、更友好的提示,解决方案
4.1.4.1 新建自定义异常类
/**
* 自定义异常
* @author Steven
*
*/
public class MyException extends Exception {
//错误消息
private String msg;
public MyException() {
super();
}
public MyException(String msg) {
super();
this.msg = msg;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
4.1.4.2 修改异常处理器,加上异常判断
package springmvc.exception;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
/**
* 全局异常处理器
*
* @author Administrator
*
*/
public class CustomerException implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response, Object handler,Exception ex) {
String msg="系统发生异常,请联系管理员";
//自定义异常处理
if(ex instanceof MyException){
msg=((MyException)ex).getMsg();
}
ModelAndView mav = new ModelAndView();
mav.addObject("msg",msg);
mav.setViewName("msg");
return mav;
}
}
4.1.4.3 测试访问queryVoid.action
/**
* 全局异常处理器+自定义异常处理
* @throws Exception
*/
@RequestMapping("exceptionQuery")
public void exceptionQuery() throws Exception{
//假设这里是根据id查询商品信息,搜索不到商品
if(true){
throw new MyException("你查找的商品不存在,请确认信息");
}
}
五、图片上传处理
5.1 配置虚拟目录
5.2 加入上传功能需要的jar包
5.3 配置多媒体解析器
<!-- 配置多媒体处理器 -->
<!-- 注意:这里id必须填写:multipartResolver -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 最大上传文件大小 -->
<property name="maxUploadSize" value="8388608" />
</bean>
5.4 itemEdit.jsp修改
5.5 编写图片上传处理代码
/**
* 修改商品
*
* 演示pojo参数绑定
*
* @return
* @throws Exception
*/
@RequestMapping("updateItem")
public String updateItem(Item item,MultipartFile pictureFile,Model model)
throws Exception{
Item itemById = itemService.getItemById(item.getId());
/* System.out.println(itemById.getPic());
System.out.println(item.getPic());*/
if(!item.getPic().equals(itemById.getPic())){
//图片新名字
String newName =UUID.randomUUID().toString();
//图片原来的名字
String oldName = pictureFile.getOriginalFilename();
//后缀
String sux=oldName.substring(oldName.lastIndexOf("."));
File file=new File("E:\\每日计划\\图片\\"+newName+sux);
// 更新商品图片信息
item.setPic(newName + sux);
//写入本地磁盘
pictureFile.transferTo(file);
}
itemService.updateItem(item);
model.addAttribute("item", item);
model.addAttribute("msg", "修改商品信息成功");
return "itemEdit";
//跳转到商品列表页面
// return "forward:itemList.action";
// return "redirect:itemList.action";
}
5.6 itemList.jsp修改
六、json数据交互
6.1 加入jar包
6.2 编码
/**
* json数据交互演示
*
* @param item2
* @return
*/
@RequestMapping("getItem")
//@ResponseBody把pojo转成json串响应用户
@ResponseBody
public Item getItem() {
Item item = itemServices.getItemById(1);
return item;
}
6.3 测试,安装google浏览器测试工具
打开网址:输入:chrome://apps
安装Advanced-REST-client_v3.1.9
将pojo转成json串
{
id: 1
name: "台式机"
price: 5000
detail: "性能好的不行"
pic: "6e6002d5-1634-4e3c-b23e-45a6efd5c66a.jpg"
createtime: 1559223587000
}
接收用户传入的json串转成pojo
@RequestMapping("getItem")
//@ResponseBody把pojo转成json串响应用户
@ResponseBody
//@RequestBody用于接收用户传入json串转成pojo
public Item getItem(@RequestBody Item item2){
System.out.println(item2);
item2.setName("手机");
// Item item = itemService.getItemById(1);
return item2;
输入json(注意格式)
接上
{
"id":1,
"name":"台式机",
"price":5000.0,
"detail":"性能好的不行",
"pic":"6e6002d5-1634-4e3c-b23e-45a6efd5c66a.jpg",
"createtime":1559223587000
}
输出
{
id: 1
name: "手机"
price: 5000
detail: "性能好的不行"
pic: "6e6002d5-1634-4e3c-b23e-45a6efd5c66a.jpg"
createtime: 1559223587000
}
七、Springmvc实现Restful
7.1 编码
/**
* RESTful风格演示
*
* @param ids
* @param model
* @return
*/
//RESTful风格url上的参数通过{}点位符绑定,因为id可变,所以用点位符
//点位符参数名与方法参数名不一致时,通过@PathVariable绑定
@RequestMapping("/item/{id}")
public String testRest(@PathVariable Integer id, Model model) {
Item item = itemService.getItemById(id);
model.addAttribute("item", item);
return "itemEdit";
}
/**
* 当接收的id与页面的id不一致时
*/
@RequestMapping("/item/{id}")
public String testRest(@PathVariable("id") Integer ids, Model model) {
Item item = itemService.getItemById(ids);
model.addAttribute("item", item);
return "itemEdit";
}
7.2 测试
测试直接访问url{http://localhost:8080/项目名/item/1.action}即可。
八、拦截器
8.1 拦截器开发流程
8.1.1 创建拦截器
8.1.1.1 一号拦截器
package springmvc.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
/**
* 定义拦截器
*
* @author Administrator
*
*/
public class MyInterceptor implements HandlerInterceptor {
/*
* 多个拦截器拦截时,如果该拦截器放行,其他不放行,则会执行该拦截器的afterCompletion方法,
* 而其他拦截器则不执行
*
* 因此,afterCompletion方法的执行与否是和拦截器是否放行一致,放行则执行,反之则不执行。
*/
// 方法执行之后被执行
// 这里可以处理异常,清理资源,记录日志等等
@Override
public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1,
Object arg2, Exception arg3)throws Exception {
System.out.println("MyInterceptor.afterCompletion.......");
}
// 方法执行之后,返回ModelAndView之前被执行
// 设置页面的共用参数等等
@Override
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1,
Object arg2, ModelAndView arg3)throws Exception {
System.out.println("MyInterceptor.postHandle.......");
}
// 进入方法之前被执行
// 这里可以用作:登录拦截,权限校验等等
@Override
public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1,
Object arg2)throws Exception {
System.out.println("MyInterceptor.preHandle.......");
// 返回true放行,false拦截
return true;
}
}
8.1.1.2 二号拦截器,复制一号,修改一下系统输出消息
8.1.2 配置拦截器
<mvc:interceptors>
<mvc:interceptor>
<!--拦截所有请求,包括二级以上目录 -->
<mvc:mapping path="/**"/>
<bean class="springmvc.interceptor.MyInterceptor" />
</mvc:interceptor>
<mvc:interceptor>
<!--"/**"表示:拦截所有请求,包括二级以上目录-->
<mvc:mapping path="/**"/>
<bean class="springmvc.interceptor.MyInterceptor2" />
</mvc:interceptor>
</mvc:interceptors>
8.1.3 输出
总结:
8.2 拦截器案例应用,登录拦截器
8.2.1 思路
1、有一个登录页面,需要写一个controller访问页面
2、登录页面有一提交表单的动作。需要在controller中处理。
- a) 判断用户名密码是否正确
- b) 如果正确 想session中写入用户信息
- c) 返回登录成功,或者跳转到商品列表
3、拦截器。
- a) 拦截用户请求,判断用户是否登录
- b) 如果用户已经登录。放行
- c) 如果用户未登录,跳转到登录页面。
8.2.2 login.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!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>用户登录</title>
</head>
<body>
<form action="${pageContext.request.contextPath }/user/login.action">
用户名:<input type="text" name="username" /><br>
密码:<input type="password" name="password" /><br>
<input type="submit">
</form>
</body>
</html>
8.2.3 UserController
package springmvc.controller;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* 用户请求处理器
*
* @author Administrator
*
*/
@Controller
@RequestMapping("user")
public class UserController {
@RequestMapping("toLogin")
public String toLogin() {
return "login";
}
@RequestMapping("login")
public String login(String username, String password, HttpSession session) {
if (username.equals("admin")) {
session.setAttribute("username", username);
//注意:加个/,因为itmeList.action不在user目录下
return "redirect:/itemList.action";
} else
return "login";
}
}
8.2.4 LoginInterceptor拦截器编码
package springmvc.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
/**
* 定义拦截器
*
* @author Administrator
*
*/
public class LoginInterceptor implements HandlerInterceptor {
// 方法执行之后被执行
// 这里可以处理异常,清理资源,记录日志等等
@Override
public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1,
Object arg2, Exception arg3)
throws Exception {
}
// 方法执行之后,返回ModelAndView之前被执行
// 设置页面的共用参数等等
@Override
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1,
Object arg2, ModelAndView arg3)
throws Exception {
}
// 进入方法之前被执行
// 这里可以用作:登录拦截,权限校验等等
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object arg2) throws Exception {
// 判断用户有没有登录
Object object = request.getSession().getAttribute("username");
if(object==null){
//System.out.println(object.toString());
System.out.println(request.getContextPath());
response.sendRedirect(request.getContextPath()+"/user/toLogin.action");
}
// 返回true放行,false拦截
return true;
}
}
8.2.5 拦截器配置
<mvc:interceptor>
<!--"/**"表示:拦截所有请求,包括二级以上目录 -->
<mvc:mapping path="/**"/>
<!--配置不拦截目录 -->
<mvc:exclude-mapping path="/user/**"/>
<bean class="springmvc.interceptor.LoginInterceptor" />
</mvc:interceptor>