SpringMVC_day02
SpringMVC_day02
1. 高级参数绑定
1.1 绑定数组
需求:接收要批量删除的商品的id。
Controller方法中可以用Integer[ ]或QueryVo的private Integer[] ids属性接收,此对象属性名要与页面name属性值相同。
controller:
public ModelAndView delItems2(QueryVo vo) {}
jsp: <c:forEach items="${itemList }" var="item">
<tr><td><input type="checkbox" name="ids" value="${item.id}"/></td>...</tr>
</c:forEach>
//页面中选中多个checkbox向controller方法传递ids
1.2 绑定List集合
需求:实现对商品数据的批量修改。
Controller方法中用QueryVo的private List
接收List类型的数据必须是pojo的属性,如果方法的形参为ArrayList类型无法正确接收到数据。
controller: public ModelAndView delItems2(QueryVo vo) {}
jsp的name属性必须是list属性名+下标+元素属性名
jsp:
<c:forEach items="${itemList }" var="item" varStatus="status">
<tr>
<td><input type="checkbox" name="itemList[${status.index }].id" value="${item.id }"/></td>
<td><input type="text" name="itemList[${status.index }].name" value="${item.name }"/></td>
...
</tr>
</c:forEach>
${current} 当前这次迭代的(集合中的)项
${status.first} 判断当前项是否为集合中的第一项,返回值为true或false
${status.last} 判断当前项是否为集合中的最
varStatus属性常用参数总结下:
${status.index} 输出行号,从0开始。
${status.count} 输出行号,从1开始。
${status.后一项,返回值为true或false
begin、end、step分别表示:起始序号,结束序号,跳跃步伐。
2. @RequestMapping
通过@RequestMapping注解可以定义不同的处理器映射规则。
2.1 URL路径映射
2.1.1 添加到方法上
@RequestMapping(value="item")或@RequestMapping("/item")/可省略
public String delItems(QueryVo vo) {}
value的值是数组,可以将多个URL映射到同一个方法。
@RequestMapping(value={"item1", "item2"})
2.1.2 添加到类上
//在class上添加@RequestMapping(url)指定通用请求前缀,限制此类下的所有方法请求url必须以请求前缀开头
@RequestMapping("/item")
public class ItemsController {}
2.2 请求方法限定
除了可以对url进行设置,还可以限定请求进来的方法。
--限定GET方法
@RequestMapping(value="/updateitem.action",method=RequestMethod.GET)
如果通过POST访问则报错:
HTTP Status 405 - Request method 'POST' not supported
--限定POST方法
@RequestMapping(value="/updateitem.action",method=RequestMethod.POST)
--限定GET和POST方法
@RequestMapping(value="/updateitem.action",method = {RequestMethod.GET,RequestMethod.POST})
不写则默认支持RequestMethod的八种方法
3. Controller方法返回值
3.1 返回ModelAndView
详见SpringMVC_day01
3.2 返回void
在Controller方法形参上可以定义request和response,使用request或response指定响应结果。
--使用request转发页面
request.getRequestDispatcher("/WEB-INF/jsp/itemList.jsp").forward(request, response);
--使用response重定向页面
response.sendRedirect("/14.2SpringMVC/success.jsp");
--使用response直接显示
response.getWriter().print("hahaha");
3.3 返回字符串
3.3.1 返回逻辑视图名
controller方法返回字符串可以指定逻辑视图名,通过视图解析器解析为物理视图地址。
//指定逻辑视图名,经过视图解析器解析为jsp路径:/WEB-INF/jsp/itemList.jsp
return "itemList";
3.3.2 redirect重定向
//转发到修改此商品页面
//重定向后地址栏变为重定向的地址,行了新的request和response,所以之前的请求参数都会丢失
return "redirect:/itemEdit.action?id="+item.getId();
3.3.3 forward转发
// 转发后地址栏还是原来的请求地址,没有执行新的request和response,之前的请求参数都存在
return "forward:/itemEdit.action";
3.4 三种返回值对比
1.ModelAndView 带着数据及返回视图路径。不建议使用,因为数据和视图耦合了。
2.String 返回视图路径,model携带数据。推荐使用,解耦,数据和视图分离。
3.void ajax请求,异步请求时使用。
4. 异常处理器
SpringMVC在处理请求过程中出现异常信息交由异常处理器进行处理,自定义异常处理器可以实现一个系统的异常处理逻辑。
4.1 异常处理思路
系统中异常包括两类:预期异常和运行时异常RuntimeException(如int i=1/0),前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。
系统的dao、service、controller出现都通过throws Exception向上抛出,最后由SpringMVC前端控制器交由异常处理器进行异常处理,如下图:
4.2 自定义异常类
为了区别不同的异常,通常根据异常类型进行区分,创建一个自定义系统异常。如果controller、service、dao抛出此类异常说明是系统预期处理的异常信息。
public class MyException extends Exception {
private String msg;//存放异常信息
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public MyException(String msg) {
super();
this.msg = msg;
}
}
4.3 自定义异常处理器
public class CustomHandlerException implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception exception) {
//自定义异常信息
String msg;
//判读异常类型
if(exception instanceof MyException) {
//如果是自定义异常,读取异常信息
msg=((MyException) exception).getMsg();
//msg=exception.getMessage();
}else {
//如果是运行时异常,则取错误堆栈,从堆栈中获取异常信息
Writer out=new StringWriter();
PrintWriter pw=new PrintWriter(out);
exception.printStackTrace(pw);
msg=out.toString();
}
ModelAndView mav=new ModelAndView();
mav.addObject("msg", msg);
mav.setViewName("exception");
return mav;
}
4.4 异常处理器配置
在springmvc.xml中添加:
<!--配置全局异常处理器-->
<bean id="customHandlerException" class="com.itheima.exception.CustomHandlerException"/>
5. 上传图片
5.1 配置图片虚拟目录
-
在tomcat上配置
在tomcat上配置图片虚拟目录,在tomcat下conf/server.xml中添加:
在浏览器访问如localhost:8080/pic/1.jpg即可访问D:\develop\upload\temp下的图片。
-
在eclipse中配置
双击tomcat,打开modules,. . .,复制一张图片到该文件夹,浏览器访问测试。
5.2 导入jar包
实现图片上传需要加入的jar包:
5.3 配置上传解析器
在springmvc.xml中配置文件上传解析器:
<!--配置上传解析器,id固定-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--设置文件上传大小限制,如为5M-->
<property name="maxUploadSize" value="5242880"/>
</bean>
5.4 修改jsp页面
form表单必须设置enctype属性:enctype="multipart/form-data"
5.5 实现图片上传
@RequestMapping("updateItems")
//MultipartFile picFile用于接收上传的图片
public String updateItems(Items item,MultipartFile picFile) throws IllegalStateException, IOException {
//设置图片名称,不能重复
String picName=UUID.randomUUID().toString();
//获取上传的图片名称
String originalName=picFile.getOriginalFilename();
//获取上传的图片后缀
//String suffix=originalName.substring(originalName.lastIndexOf("."));//.jpg
String suffix=FilenameUtils.getExtension(originalName);//jpg
//保存图片
picFile.transferTo(new File("D:\\JavaEE\\04.Tomcat\\img\\"+picName+"."+suffix));
//设置图片名到商品中
item.setPic(picName+"."+suffix);
//更新商品
this.itemsService.updateById(item);
//转发到修改此商品页面
return "redirect:/itemEdit.action?id="+item.getId();
}
6. json数据交互
6.1 @RequestBody, @ResponseBody
/*
@RequestBody注解接收http请求的json数据,将json数据转换为java对象进行绑定。
@ResponseBody注解用于将Controller的方法返回的对象,通过springmvc提供的HttpMessageConverter接口转换为指定格式的数据如:json,xml等,通过Response响应给客户端。
*/
@RequestMapping("testJson")
public @ResponseBody Items testJson(@RequestBody Items item) {
//@ResponseBody设置将返回的数据转为json格式
//@RequestBody将传入的json数据封装到Items对象上
return item;
}
6.2 SpringMVC加入json支持
导入jar包:
6.3 配置json转换器
在springmvc.xml中
--method1:使用注解驱动
<mvc:annotation-driven />
--method2:不适用注解驱动
<!--处理器适配器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class=
"org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"></bean>
</list>
</property>
</bean>
7. RESTful
7.1 什么是RESTful
一种资源定位及资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
资源:互联网所有的事物都可以被抽象为资源
资源操作:使用POST、DELETE、PUT、GET,使用不同方法对资源进行操作。分别对应 添加、 删除、修改、查询。
传统方式操作资源
http://127.0.0.1/item/queryItem.action?id=1 查询,GET
http://127.0.0.1/item/saveItem.action 添加,POST
http://127.0.0.1/item/updateItem.action 修改,POST
http://127.0.0.1/item/deleteItem.action?id=1 删除,GET或POST
使用RESTful操作资源
http://127.0.0.1/item/1 查询,GET
http://127.0.0.1/item 添加,POST
http://127.0.0.1/item 修改,PUT
http://127.0.0.1/item/1 删除,DELETE
传统方式每次请求的地址都在做描述,如查询时是queryItem,添加时是saveItem,修改时是updateItem,删除时是deleteItem。为了不做描述,所以使用RESTful风格。
7.2 RESTful演示
使用RESTful风格开发的接口,根据id查询商品,接口地址是:localhost:8080/item/1,从url上获取商品id,步骤如下:
-
使用注解@RequestMapping("item/{id}")声明请求的url;{xxx}叫做占位符
-
使用(@PathVariable() Integer id)获取url上的数据
@RequestMapping("/itemEdit/{id}.action")
public ModelAndView queryById(@PathVariable Integer id) {}
/*
如果@RequestMapping中表示为"item/{id}",id和形参名称一致,@PathVariable不用指定名称。如果不一致,例如"item/{ItemId}"则需要指定名称@PathVariable("itemId")
*/
//浏览器访问localhost:8080/14.2SpringMVC/itemEdit/1.action
注意两个区别
-
@PathVariable是获取url上数据的。@RequestParam获取请求参数的(包括post表单提交)
-
如果加上@ResponseBody注解,就不会走视图解析器,不会返回页面,目前返回的json数据。如果不加,就走视图解析器,返回页面。
8. 拦截器
8.1 定义
SpringMVC 的处理器拦截器类似于Servlet 开发中的过滤器Filter,用于对处理器进行预处理和后处理。
8.2 自定义拦截器类
public class HandlerInterceptor1 implements HandlerInterceptor {
//controller执行前调用,return true表示继续执行,false终止执行,
//这里可以加入登录校验,权限拦截等。
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception {
return true;
}
//controller执行后但视图未返回前调用,
//这里可以对模型数据进行加工处理,如加入公用信息以便页面显示。
@Override
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
throws Exception {
System.out.println("1_postHandle");
}
//controller执行且视图返回后调用,
//这里可得到执行controller时的异常信息,也可记录操作日志
@Override
public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
throws Exception {
System.out.println("1_afterCompletion");
}
}
8.3 拦截器配置
在springmvc中配置拦截器:
<mvc:interceptors>
<!--配置拦截器1-->
<mvc:interceptor>
<!--设置所有请求都进入拦截器
"/*" 是拦截所有的文件夹,不包含子文件夹
"/**" 是拦截所有的文件夹及里面的子文件夹-->
<mvc:mapping path="/**"/>
<bean class="com.itheima.handlerInterceptor.HandlerInterceptor1"></bean>
</mvc:interceptor>
<!--配置拦截器2-->
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.itheima.handlerInterceptor.HandlerInterceptor2"></bean>
</mvc:interceptor>
</mvc:interceptors>
8.4 测试
浏览器访问localhost:8080/14.2SpringMVC/itemList.action
HandlerInterceptor1的preHandler方法返回false,HandlerInterceptor2返回true
运行结果如下:
1_preHandle
从日志看出第一个拦截器的preHandler方法返回false后第一个拦截器只执行了preHandler方法,其它两个方法没有执行,第二个拦截器的所有方法不执行,且Controller也不执行了。
HandlerInterceptor1的preHandler方法返回true,HandlerInterceptor2返回false
运行结果如下:
1_preHandle
2_preHandle
1_afterCompletion
从日志看出第二个拦截器的preHandler方法返回false后第一个拦截器的postHandler没有执行,第二个拦截器的postHandler和afterCompletion没有执行,且controller也不执行了。
HandlerInterceptor1的preHandler方法返回true,HandlerInterceptor2返回true
运行结果如下:
1_preHandle
2_preHandle
2_postHandle
1_postHandle
2_afterCompletion
1_afterCompletion
preHandle按拦截器定义顺序调用,postHandler按拦截器定义逆序调用,afterCompletion按拦截器定义逆序调用。
postHandler在拦截器链内所有拦截器返成功调用,afterCompletion只有preHandle返回true才调用。
拦截器定义顺序取决于springmvc配置的顺序
9. 拦截器应用
需求: 拦截用户请求,判断用户是否登录(登录请求不能拦截),如果用户已经登录,放行;如果用户未登录,跳转到登录页面。
public class HandlerInterceptor1 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception {
//判断用户是否登录,如果没有登录,重定向到登录页面,不放行;如果登录,放行
//url:http://localhost:8080/14.2SpringMVC/login.action
//uri:/login.action
//注意:不拦截登录请求(即uri中包含"login"的)
String uri=request.getRequestURI();
if(!uri.contains("login")) {
//获取用户名
String username=(String)request.getSession().getAttribute("username");
//判断用户名是否为空
if(username==null) {
//不放行,跳转到登录界面
response.sendRedirect(request.getContextPath()+"/toLogin.action");
return false;
}
}
return true;
}
...
}