大纲摘要:

    1、高级参数绑定

      a) 数组类型的参数绑定

      b) List类型的绑定

    2、@RequestMapping注解的使用

    3、Controller方法返回值

    4、Springmvc中异常处理

    5、图片上传处理

    6、Json数据交互

    7、Springmvc实现Restful

    8、拦截器

一、高级参数绑定

  (1)数组类型

  1.需求:

    在商品列表页面选中多个商品,然后删除。

  从需求可以看出,这需要用到数组类型的参数绑定

  正式开始之前复制昨天的ssm-01项目,这里再提一点之前提到的要改项目名(web project settings),改context root

  修改jsp页面,将id传递过去,使用的是checkbox,当选中时将value值传递过去

<c:forEach items="${itemList }" var="item">
<tr>
<td><input type="checkbox" name="ids" value="${item.id }"/></td>
<td>${item.name }</td>
<td>${item.price }</td>
<td><fmt:formatDate value="${item.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/></td>
<td>${item.detail }</td> <td><a href="${pageContext.request.contextPath }/itemEdit.action?id=${item.id}">修改</a></td> </tr>
</c:forEach>

  把from提交的action路径改一下

<form action="${pageContext.request.contextPath }/delAll.action" method="post">

  这样ItemList.jsp就改完了,把上面delAll这个action完成一样:

//批量删除的方法
@RequestMapping("/delAll")
public String delAll(QueryVo vo) throws Exception{
//一般不会单独接收某一个参数,而是采用接收vo的形式,以便拓展等
//类似这种批量删除一堆的,可以采用只用checkbox被选中才被传递进来,并在此处使用vo里的数组接收
System.out.println(vo);
return "";
}

  当然,这里可以用工具看一下生成的HTML代码,结构更加清晰:

<table width="100%" border=1>
<tr>
<td>商品名称</td>
<td>商品价格</td>
<td>生产日期</td>
<td>商品描述</td>
<td>操作</td>
</tr>
<tr>
<td><input name="ids" value="1" type="checkbox"></td>
<td>台式机</td>
<td>3000.0</td>
<td>2016-02-03 13:22:53</td>
<td></td>
<td><a href="/springmvc-web/itemEdit.action?id=1">修改</a></td>
</tr>
<tr>
<td><input name="ids" value="2" type="checkbox"></td>
<td>笔记本</td>
<td>6000.0</td>
<td>2015-02-09 13:22:57</td>
<td></td>
<td><a href="/springmvc-web/itemEdit.action?id=2">修改</a></td>
</tr>
<tr>
<td><input name="ids" value="3" type="checkbox"></td>
<td>背包</td>
<td>200.0</td>
<td>2015-02-06 13:23:02</td>
<td></td>
<td><a href="/springmvc-web/itemEdit.action?id=3">修改</a></td>
</tr>
</table>

  //这里就只采用debug方式看一下数据是否正确传递进来了,这里用到的是不直接接收ids,而是采用数组形式接收,数组封装在vo中:

package cn.vo;

import cn.pojo.Items;

public class QueryVo {

    //商品对象
private Items items;
public Items getItems() {
return items;
}
public void setItems(Items items) {
this.items = items;
}
//订单对象 用户对象...
//批量删除时传递过来的ids,采用封装到vo的方式
private Integer[] ids;
public Integer[] getIds() {
return ids;
}
public void setIds(Integer[] ids) {
this.ids = ids;
} }

  //其它的属性暂时不管,这里看vo封装的ids

  (2)集合list类型

  1.需求:

    实现商品数据的批量修改。

   可以在表单页面批量修改,然后批量提交修改后的数据

    这里我们还是把接收的类型放在vo中

//批量修改时用来接收的list集合
private List<Items> itemsList;
public List<Items> getItemsList() {
return itemsList;
}
public void setItemsList(List<Items> itemsList) {
this.itemsList = itemsList;
}

  然后,修改一下itemsList.jsp页面:

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<!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 }/updateAll.action" method="post">
查询条件:
<table width="100%" border=1>
<tr>
<!-- 使用了包装类型vo后,将使用对象.xx的形式编写name属性值 -->
商品名称:<td><input type="text" name="items.name"/></td>
商品价格:<td><input type="text" name="items.price"/></td>
<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>
</tr>
<!-- 批量修改操作时,可以使用List<pojo>接收,list封装在vo中
而input中属性值为vo的相关list属性名称和下标.list泛型属性名称
也就是例如:itemsList[${status.index }].id
vo中集合为:private List<Items> itemsList;
-->
<c:forEach items="${itemList }" var="item" varStatus="status">
<tr>
<td>
<input type="checkbox" name="ids" value="${item.id }"/>
<input type="hidden" name="itemsList[${status.index }].id" value="${item.id }"/>
</td>
<td><input type="text" name="itemsList[${status.index }].name" value="${item.name }"/></td>
<td><input type="text" name="itemsList[${status.index }].price" value="${item.price }"/></td>
<td><input type="text" name="itemsList[${status.index }].createtime" value="<fmt:formatDate value='${item.createtime}' pattern='yyyy-MM-dd HH:mm:ss'/>"/></td>
<td><input type="text" name="itemsList[${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>
</body> </html>

  //这里注意的是:

    1.sumbit提交改成提交到updateAll

<td><input type="submit" value="批量修改"/></td>

    2.注意list的写法(注意通过varStatus控制循环变量下标信息index):

<td><input type="text" name="itemsList[${status.index }].name" value="${item.name }"/></td>

    3.通过隐藏域将id传过去:

<input type="hidden" name="itemsList[${status.index }].id" value="${item.id }"/>

   再将action按前面的方式编写完成,这里也只做测试:

//批量修改的方法
@RequestMapping("/updateAll")
public String updateAll(QueryVo vo) throws Exception{
System.out.println(vo);
return "";
}

   其它几个参数相关的注解,用法都是类同

 这里先介绍一下@RequestParam;主要作用是前面接收基本类型时,基本类型的变量名称就可以自定义,

  而不必与前台页面name属性值一致。还可以配置除了value外,还可以配置required defaultValue等属性

public String updateitem(@RequestParam("id") Integer idd,
String name,Float price,String detail) throws Exception{

  //当然,看得出这方式略显麻烦,企业中用的不多,作【了解】

  其它的兄弟包括有 @RequestHeader @CookieValue (@PathVariable) 等,

  分别请求请求头信息、cookie信息等,这里不再展开赘述,用法也都类同

二、Requestmapping注解的使用

  1. URL路径映射

  @RequestMapping(value="/item")或@RequestMapping("/item)

  value的值是数组,可以将多个url映射到同一个方法

  了解:可以支持ant风格的通配符的形式(/?,/*,/**)

  2.窄化请求映射

  在class上添加@RequestMapping(url)指定通用请求前缀, 限制此类下的所有方法请求url必须以请求前缀开头,通过此方法对url进行分类管理。

  如下:

    @RequestMapping放在类名上边,设置请求前缀

    @Controller

    @RequestMapping("/item")

  3.请求方法映射

  限定GET方法

    @RequestMapping(method = RequestMethod.GET)

  如果通过Post访问则报错:

    HTTP Status 405 - Request method 'POST' not supported

    例如:

      @RequestMapping(value="/editItem",method=RequestMethod.GET)

   限定POST方法

      @RequestMapping(method = RequestMethod.POST)

  如果通过Post访问则报错:

    HTTP Status 405 - Request method 'GET' not supported

   GET和POST都可以

      @RequestMapping(method={RequestMethod.GET,RequestMethod.POST})

//回忆注解部分,只有只有value属性时才可以只写值不写value=

    RequestMapping里的value映射url,methiod映射方法,params映射参数(类似@RequestParam可以映射请求参数),

      当然由示例知道这种形式使用多

三、controller方法返回值

  1.返回 ModelAndView

    controller方法中定义ModelAndView对象并返回,对象中可添加model数据、指定view(当然也可以在构造modelAndView时使用viewName作为构造器参数直接传入)。

@Autowired
private ItemsService service;
@RequestMapping(value="/list",method=RequestMethod.GET)
public ModelAndView itemsList() throws Exception{
List<Items> list = service.list();
//需要modelAndView
ModelAndView modelAndView = new ModelAndView();
//加模型
modelAndView.addObject("itemList", list);
//加要去的视图路径
modelAndView.setViewName("itemList");
return modelAndView;
}

    核心代码如下:

      //加模型
modelAndView.addObject("itemList", list);
//加要去的视图路径
modelAndView.setViewName("itemList");

  更新【处理模型数据】:除了可以通过modelAndView添加数据外,还可以有以下几种形式:

    1.定义ModelAndView添加模型数据并返回,这里上文已作演示

    2.入参为Map(注意添加泛型)或Model,使用map.put将数据放入,页面即可取到

@RequestMapping("/map")
public String testMap(Map<String,Object> map){
map.put("names", Arrays.asList("tom","jack"));
return "list";
}

      model类型上文有提到,这里贴出核心代码:

model.addAttribute("item", items);

    3.使用@SessionAttributes,可以将某个属性暂存到session中,实现跨请求共享数据

      使用方式是在controller类上添加注解:

@SessionAttributes({"items"})
@Controller
public class ItemsController {

      //参数value是String[]数组类型,type属性可以规定类型 value={},types={String.class}

      在方法中当map.put的key和注解的value相同时,不仅会放在request域中,还会放在session中:

@RequestMapping("/session")
public String testSessionAttr(Map<String,Object> map){
Items items = new Items();
map.put("items", items);
return "";
}

   SessionAttribute引发的异常:session找不到属性(可见源码分析处),当@SessionAttribute中的value和ModelAttribute中的value一致时

就会抛出异常,它会强制从session中查找。

     4.使用@ModelAndAttribute,可以有如下的用法:

      【更新】:更多此注解的用法,参见http://blog.csdn.net/xiejx618/article/details/43638537

      请注意示例典型用法:

    @ModelAttribute
public OaNotify get(@RequestParam(required=false) String id) {
OaNotify entity = null;
if (StringUtils.isNotBlank(id)){
entity = oaNotifyService.get(id);
}
if (entity == null){
entity = new OaNotify();
}
return entity;
}

    简要分析:

      当执行修改时,先执行此方法,按ID查出要改的对象,返回给指定的页面进行表单数据回显

     当执行新增时,同样先执行此方法,按ID查,查出来的必为null,然后如果直接返回Null,对象,进入add()方法的入参则为null(因为在两个方法之间无法正确的进行参数绑定),当手动new一个对象时,有对应的name(只是值为空而已,在走完此方法去add方法之间会进行表单数据的绑定)

      1.情景:例如完成修改操作,但是有一个字段不能被修改(例如录入时间等)

         我们之前的做法是new 一个对象,给它赋值(不能修改的字段不能赋值,就为空了),拿着这个有字段为空的对象来修改时不行的

      如果使用我们的思路,可以使用隐藏域,但这样如果是敏感信息就不能用了,如果更新之前先从数据库查,又比较麻烦,这里可以使用这个

      注解来更加优雅地解决

      我们需要的是有一个字段是从数据库获取的不能修改的,而其他的是外界传入的。图解如下:

  //我们要修改的这个对象呢,是从数据库拿的(不能修改的字段也已经有值了),而不是外界new的

    原始的方式就不做赘述:

@RequestMapping("/model")
public String testmodelAttr(Items items){
//传入items对象进行调用Service Dao等进行修改
return "";
}

    这里我们看方法上使用注解(也可以使用在入参上):

@ModelAttribute
public void testmodelAttr(@RequestParam(value="id",required=false) Integer id,
Map<String,Object> map) throws Exception{
if(id != null){//id由页面从隐藏域传来
//从数据库中findById获取一个对象
Items items = service.findItemsById(id);
map.put("items", items);
}
}

  //这里只使用上面的一个注解,因为方法上使用注解,springMVC会在调用目标方法前,逐个调用加了@ModelAttribute注解的方法

源码分析不展开:

/**
* 运行流程:
* 1. 执行 @ModelAttribute 注解修饰的方法: 从数据库中取出对象, 把对象放入到了 Map 中. 键为: user
* 2. SpringMVC 从 Map 中取出 User 对象, 并把表单的请求参数赋给该 User 对象的对应属性.
* 3. SpringMVC 把上述对象传入目标方法的参数.
*
* 注意: 在 @ModelAttribute 修饰的方法中, 放入到 Map 时的键需要和目标方法入参类型的第一个字母小写的字符串一致!
*
* SpringMVC 确定目标方法 POJO 类型入参的过程
* 1. 确定一个 key:
* 1). 若目标方法的 POJO 类型的参数木有使用 @ModelAttribute 作为修饰, 则 key 为 POJO 类名第一个字母的小写
* 2). 若使用了 @ModelAttribute 来修饰, 则 key 为 @ModelAttribute 注解的 value 属性值.
* 2. 在 implicitModel 中查找 key 对应的对象, 若存在, 则作为入参传入
* 1). 若在 @ModelAttribute 标记的方法中在 Map 中保存过, 且 key 和 1 确定的 key 一致, 则会获取到.
* 3. 若 implicitModel 中不存在 key 对应的对象, 则检查当前的 Handler 是否使用 @SessionAttributes 注解修饰,
* 若使用了该注解, 且 @SessionAttributes 注解的 value 属性值中包含了 key, 则会从 HttpSession 中来获取 key 所
* 对应的 value 值, 若存在则直接传入到目标方法的入参中. 若不存在则将抛出异常.
* 4. 若 Handler 没有标识 @SessionAttributes 注解或 @SessionAttributes 注解的 value 值中不包含 key, 则
* 会通过反射来创建 POJO 类型的参数, 传入为目标方法的参数
* 5. SpringMVC 会把 key 和 POJO 类型的对象保存到 implicitModel 中, 进而会保存到 request 中.
*
* 源代码分析的流程
* 1. 调用 @ModelAttribute 注解修饰的方法. 实际上把 @ModelAttribute 方法中 Map 中的数据放在了 implicitModel 中.
* 2. 解析请求处理器的目标参数, 实际上该目标参数来自于 WebDataBinder 对象的 target 属性
* 1). 创建 WebDataBinder 对象:
* ①. 确定 objectName 属性: 若传入的 attrName 属性值为 "", 则 objectName 为类名第一个字母小写.
* *注意: attrName. 若目标方法的 POJO 属性使用了 @ModelAttribute 来修饰, 则 attrName 值即为 @ModelAttribute
* 的 value 属性值
*
* ②. 确定 target 属性:
* > 在 implicitModel 中查找 attrName 对应的属性值. 若存在, ok
* > *若不存在: 则验证当前 Handler 是否使用了 @SessionAttributes 进行修饰, 若使用了, 则尝试从 Session 中
* 获取 attrName 所对应的属性值. 若 session 中没有对应的属性值, 则抛出了异常.
* > 若 Handler 没有使用 @SessionAttributes 进行修饰, 或 @SessionAttributes 中没有使用 value 值指定的 key
* 和 attrName 相匹配, 则通过反射创建了 POJO 对象
*
* 2). SpringMVC 把表单的请求参数赋给了 WebDataBinder 的 target 对应的属性.
* 3). *SpringMVC 会把 WebDataBinder 的 attrName 和 target 给到 implicitModel.
* 近而传到 request 域对象中.
* 4). 把 WebDataBinder 的 target 作为参数传递给目标方法的入参.
*/

  一部分属性来自数据库取出的,一部分属性来自表单,springMVC从map中取出,这里map.put的key键值必须与方法的入参对应类名第一个字母小写

这里就是 map.put(items,...),是由于POJO类名为Items,如果不一致呢,这里就可以使用此注解修饰方法的入参,若使用了  @ModelAttribute 来修饰, 则 key 为 @ModelAttribute 注解的 value 属性值,比如:@ModelAttribute("attrName") Items items,attrName即为map中的key

public String updateitem(@ModelAttribute("attrName") Items items) throws Exception{

   2.返回String

    返回的是逻辑视图名去掉拓展名(当然还有配置指定的前后缀有相关的配置),返回页面的数据可以通过model或者参数类型等指定

    这里的返回String感觉是和strtus2非常类似,比较容易上手。带转发重定向的特殊操作见下文

//点击修改到详情页
@RequestMapping("/itemEdit")
public String itemEdit(HttpServletRequest request,HttpServletResponse response,
HttpSession session,Model model) throws Exception{
//原始的通过request拿参数
String id = request.getParameter("id");
Items items = service.findItemsById(Integer.parseInt(id));
//model叫做模型,模型中放入返回给页面的数据
//底层用的就是request域来传递数据,但是进行了拓展
model.addAttribute("item", items);
//如果springMVC方法返回一个简单的string字符串
//springMVC就是认为这就是页面的名称
return "editItem";
}

   3.返回void

   返回页面的数据可以通过request.setAttribute()等方法设置返回页面的数据

  在controller方法形参上可以定义request和response,使用request或response指定响应结果:

    1、使用request转向页面,如下:

      request.getRequestDispatcher("页面路径").forward(request, response);

    2、也可以通过response页面重定向:

      response.sendRedirect("url")

    3、也可以通过response指定响应结果,例如响应json数据如下:

      response.setCharacterEncoding("utf-8");

      response.setContentType("application/json;charset=utf-8");

      response.getWriter().write("json串");

  //这里就和WEB的阶段的是基本一致,这里就不再举例了。这里不会走springMVC的组件,路径需要全路径!

  (似乎和springMVC联系就不大了,应该是只做了解演示)

  请求转发和重定向:

    这里暂时就不赘述转发和重定向的区别(见WEB部分)

    我们可以通过打断点,查看调用栈来看大致原理。

    场景是:一般更新后,一般可以走list的action去list的页面,这里有点像struts2里的重定向action

  controller方法执行后继续执行另一个controller方法,如下商品修改提交后转向到商品修改页面,修改商品的id参数可以带到商品修改方法中。

//点击修改到详情页
@RequestMapping("/itemEdit")
public String itemEdit(HttpServletRequest request,HttpServletResponse response,
HttpSession session,Model model) throws Exception{
//原始的通过request拿参数
String id = request.getParameter("id");
Items items = service.findItemsById(Integer.parseInt(id));
//model叫做模型,模型中放入返回给页面的数据
//底层用的就是request域来传递数据,但是进行了拓展
model.addAttribute("item", items);
//如果springMVC方法返回一个简单的string字符串
//springMVC就是认为这就是页面的名称
return "editItem";
}
//修改页面数据的方法(记得配requestMapping)
//public String updateitem(Integer id,
//String name,Float price,String detail) throws Exception{
@RequestMapping("/updateitem")
public String updateitem(Items items, Model model) throws Exception{
//由于字段限制非空,必须加时间(后面配置转换器后不需要了)
/*items.setCreatetime(new Date());*/
service.updateitem(items);
model.addAttribute("id", items.getId());
return "forward:itemEdit.action";
}

  //这里需要注意的是,转发时的逻辑的处理(Id的传递)

  这里还需要特别注意转发时路径的编写

return "forward:itemEdit.action";

  //像这里就是不加 / 的相对路径(相对的是当前的类路径)

  如果要转到其他的controller(其他类的方法),需要使用绝对路径:

return "forward:/items/itemEdit.action";

  //绝对路径是从项目名目录开始,这里假设此controller使用了items的窄化映射

  当然,有转发就有重定向,使用方法是类同:

//重定向到queryItem.action地址,request无法带过去
return "redirect:queryItem.action";

  //这里我们知道,重定向是两次请求,request域里的数据是只能保存一次请求的,如果直接使用request.setAttribute(),将会失效

  //之前说过的model对request进行了增强,它就可以保存重定向后的数据!

     model.addAttribute("id", items.getId());
return "redirect:itemEdit.action";

  出现NumberFormatException原因就是:试图把null值进行转换,也就是传值时值丢失了。

  若希望直接响应JSP页面而不通过controller,(因为jsp在WEB-INF下,无法由客户端跳转),可以通过mvc:view-controller跳转:

<!-- 配置直接响应的页面 -->
<mvc:view-controller path="/success" view-name="success"/>

  //可以直接从外部访问WEB-INF,不用经过controller,直接在springMVC核心配置文件配置即可

  但是这样的话,之前的访问就会报404,需要配置i<mvc annotation-driven> (虽然我们通常会配)

 四、Springmvc中异常处理

  这里所说的异常呢,是springMVC的架构级别的异常,用于处理比方说一些全局的异常

  springmvc在处理请求过程中出现异常信息交由异常处理器进行处理,由HandlerExceptionResolver自定义异常处理器可以实现一个系统的异常处理逻辑。

  这里我们重点是看第一个实现类:

  1.异常处理思路

  系统中异常包括两类:预期异常和运行时异常RuntimeException,前者通过捕获异常从而获取异常信息,

    后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。

  系统的dao、service、controller出现都通过throws Exception向上抛出,最后由springmvc前端控制器交由异常处理器进行异常处理

  Spring MVC处理异常有3种方式: 
  (1)使用Spring MVC提供的简单异常处理器SimpleMappingExceptionResolver; 
  (2)实现Spring的异常处理接口HandlerExceptionResolver 自定义自己的异常处理器; 
  (3)使用@ExceptionHandler注解实现异常处理;

 异常处理实战:

  方式1:在spring的配置文件中增加相关的配置(这里是在sprinMVC的配置文件中增加),主要是配置这个bean

<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<!-- 定义默认的异常处理页面,当该异常类型的注册时使用 -->
<property name="defaultErrorView" value="error"></property>
<!-- 定义异常处理页面用来获取异常信息的变量名,默认名为exception -->
<property name="exceptionAttribute" value="ex"></property>
<!-- 定义需要特殊处理的异常,用类名或完全路径名作为key,异常也页名作为值 -->
<property name="exceptionMappings">
<props>
<prop key="cn.basttg.core.exception.BusinessException">error-business</prop>
<prop key="cn.basttg.core.exception.ParameterException">error-parameter</prop> <!-- 这里还可以继续扩展对不同异常类型的处理 -->
</props>
</property>
</bean>

  实际配置:

<!-- 异常处理的bean -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="org.apache.shiro.authz.UnauthorizedException">error/403</prop>
<prop key="java.lang.Throwable">error/500</prop>
</props>
</property>
</bean>

//prop的值为error页的路径(注意实际路径,也就是配上springMVC视图解析器的前后缀的路径)

  404等错误的配置:

我们可能不得不面对尴尬的404、500……等服务器内部错误提示页面。 
我们需要一个全面而有效的异常处理机制。目前大多数服务器也都支持在Web.xml中通过<error-page>(Websphere/Weblogic)或者<error-code>(Tomcat)节点配置特定异常情况的显示页面。修改web.xml文件,增加以下内容:

<error-page>
<error-code>500</error-code>
<location>/WEB-INF/views/error/500.jsp</location>
</error-page>
<error-page>
<error-code>404</error-code>
<location>/WEB-INF/views/error/404.jsp</location>
</error-page>

 2.自定义异常类(可选)

  为了区别不同的异常通常根据异常类型自定义异常类,这里我们创建一个自定义系统异常,如果controller、service、dao抛出此类异常说明是系统预期处理的异常信息。

public class CustomException extends Exception {

    /** serialVersionUID*/
private static final long serialVersionUID = -5212079010855161498L; public CustomException(String message){
super(message);
this.message = message;
} //异常信息
private String message; public String getMessage() {
return message;
} public void setMessage(String message) {
this.message = message;
}
}

  //通过继承Exception来实现

  3.自定义异常处理器

public class CustomExceptionResolver implements HandlerExceptionResolver {

    @Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) { ex.printStackTrace(); CustomException customException = null; //如果抛出的是系统自定义异常则直接转换
if(ex instanceof CustomException){
customException = (CustomException)ex;
}else{
//如果抛出的不是系统自定义异常则重新构造一个系统错误异常。
customException = new CustomException("系统错误,请与系统管理 员联系!");
} ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("message", customException.getMessage());
modelAndView.setViewName("error"); return modelAndView;
} } 取异常堆栈:
try { } catch (Exception e) {
StringWriter s = new StringWriter();
PrintWriter printWriter = new PrintWriter(s);
e.printStackTrace(printWriter);
s.toString();
}

  4.错误页面 error.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<!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>
您的操作出现错误如下:<br/>
${message }
</body> </html>

  5.在配置文件springMVC.xml中配置异常处理器

<!-- 异常处理器 -->
<bean id="handlerExceptionResolver" class="cn.itcast.ssm.controller.exceptionResolver.CustomExceptionResolver"/>
//处理异常的handler
@ExceptionHandler(ArithmeticException.class)
public String testHandlerException(Exception e){
System.out.println("出现算术异常!异常信息是:"+e);
return "error";
}
@RequestMapping("/testExceptionHandlerExceptionResolver")
public String testExceptionHandlerExceptionResolver(
@RequestParam("i") Integer i){
//将抛出异常
System.out.println("result:"+i/0);
return "error";
}

 //通过注解简化

    6.异常测试

   修改商品信息,id输入错误提示商品信息不存在。

  修改controller方法“editItem”,调用service查询商品信息,如果商品信息为空则抛出异常:

// 调用service查询商品信息
Items item = itemService.findItemById(id); if(item == null){
throw new CustomException("商品信息不存在!");
}

  //在service中抛出异常方法同上。

**这里暂时参见:http://blog.csdn.net/ufo2910628/article/details/40399539 (视频中断了,待补充)

 五、图片上传处理

  图片上传是通过 multipatrResolver(ctrl shift t查看)

  1.配置虚拟目录

      一般都需要一个专门的服务器之类的存放图片这种占地方的文件,这里我们使用tomcat直接进行配置:

  双进tomcat,点击添加模块:

  这里进行简单的配置(这里path就相当于一个项目名):

  配置好之后进行校验一下(因为此时图片服务器应该是对外可以访问的,我们这里输入访问路径,成功访问图片即为成功!):

    http://localhost:8080/pic/a.jpg

  当然,不在eclipse中也是可以手动配置的:

  在tomcat上配置图片虚拟目录,在tomcat下conf/server.xml中添加:

  <Context docBase="F:\develop\upload\temp" path="/pic" reloadable="false"/>

  访问http://localhost:8080/pic即可访问F:\develop\upload\temp下的图片。

  2.配置好后我们把文件上传的经典包导一下:

【更新】:maven的fileupload引入:

<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>

  3.配置解析器

  在springMVC.xml中进行配置:——也就是配置MultipartResolver(也可以配置默认编码)

//用了springIDE后,美滋滋

<!-- 文件上传 -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 设置上传文件的最大尺寸为5MB -->
<property name="maxUploadSize">
<value>5242880</value>
</property>
</bean>

  4.修改JSP页面

   把editItem.jsp打开,先把上传图片的注释放出来。

  然后,重要的一点,凡是上传图片的,一定要改表单的enctype!

<form id="itemForm"    action="${pageContext.request.contextPath }/updateitem.action"
method="post" enctype="multipart/form-data">

  修改完后的jsp页面如下:

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<!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>
<!-- 上传图片是需要指定属性 enctype="multipart/form-data" -->
<!-- <form id="itemForm" action="" method="post" enctype="multipart/form-data"> -->
<form id="itemForm" action="${pageContext.request.contextPath }/updateitem.action"
method="post" enctype="multipart/form-data">
<input type="hidden" name="id" value="${item.id }" /> 修改商品信息:
<table width="100%" border=1>
<tr>
<td>商品名称</td>
<td><input type="text" name="name" value="${item.name }" /></td>
</tr>
<tr>
<td>商品价格</td>
<td><input type="text" name="price" value="${item.price }" /></td>
</tr> <tr>
<td>商品生产日期</td>
<td><input type="text" name="createtime"
value="<fmt:formatDate value="${item.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/>" /></td>
</tr> <tr>
<td>商品图片</td>
<td>
<c:if test="${item.pic !=null}">
<img src="/tomcat_pic/${item.pic}" width=100 height=100/>
<br/>
</c:if>
<input type="file" name="pictureFile"/>
</td>
</tr> <tr>
<td>商品简介</td>
<td><textarea rows="3" cols="30" name="detail">${item.detail }</textarea>
</td>
</tr>
<tr>
<td colspan="2" align="center"><input type="submit" value="提交" />
</td>
</tr>
</table> </form>
</body> </html>

  5.编写controller

@RequestMapping("/updateitem")
public String updateitem(MultipartFile pictureFile,Items items, Model model) throws Exception{
//由于字段限制非空,必须加时间(后面配置转换器后不需要了)
/*items.setCreatetime(new Date());*/
service.updateitem(items);
//获取图片完整名称
String fileStr = pictureFile.getOriginalFilename();
//使用随机生成的字符串加原图片名组成新图片的名称,进而防止文件重名(UUID)
String uuid = UUID.randomUUID().toString();
String newFilename = uuid + fileStr.substring(fileStr.lastIndexOf("."));
//将图片保存到 硬盘
pictureFile.transferTo(new File("F:/tomcat_pic/"+newFilename));
//将图片名称保存到数据库
items.setPic(newFilename);
model.addAttribute("id", items.getId());
return "redirect:itemEdit.action";
}

  //这里注意的是MultipartFile,参数名称就是表单中上传文件的name属性值(当然可以通过@RequestParam()进行自定义)。 @RequestParam(value = "file1", required = false) MultipartFile file1,

    这个接口中有很多方便的方法,我们可以在真正进行执行之前做必要的文件是否是空的判断:if(!ifle.isEmpty()){}

  //这里打开F://tomcat_pic文件夹可以看到成功上传的文件。

  多图片上传请参考:http://blog.csdn.net/luckey_zh/article/details/46867957

   可以由此拓展上传文件,在controller中入参 @RequestParam("file" MutipartFile file)...

 六、JSON数据交互

  ajax请求与springMVC的配合案例可参考(遍历返回的JSON请参考JSON随笔): 

    参考1:   http://blog.csdn.net/yixiaoping/article/details/45281721

    参考2:   http://blog.csdn.net/CarryBest/article/details/71214111

  准备工作:

    JSON是什么?是一种数据交互的格式

  回忆前面的部分,和JSON交互需要使用 jackson 的包

//一个注解包、一个核心包、数据绑定包 【更新】:maven中只需定义第三个数据包,后两个依赖包将会自动载入

  在注解适配器中加入messageConverters

<!--注解适配器 -->
<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>

  注意:如果使用<mvc:annotation-driven /> 则不用定义上边的内容。

这里在webContent下建立js的folder,(不在WEB-INF安全目录下),外界可以直接访问,然后导支持jQuery的核心包,并在页面导入

<script type="text/javascript" src="${pageContext.request.contextPath }/js/jquery-1.4.4.min.js"></script>

  //这里注意引入的src处的路径的写法(使用标准的绝对路径)

  这里把前台的ajax完成一下(复习一下jQuery的ajax操作):

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<!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>
<script type="text/javascript" src="${pageContext.request.contextPath }/js/jquery-1.4.4.min.js"></script>
</head>
<body>
<script type="text/javascript">
function sendJson(){
$.ajax({
type:"post",
url:"${pageContext.request.contextPath }/sendJson.action",
contentType:"application/json;charset=utf-8",
data:'{"name":"测试商品","price":99.9}',
success:function(data){
alert(data);
}
});
}
</script>
<input type="button" value="sendJson" onclick="sendJson()"/>
<form action="${pageContext.request.contextPath }/updateAll.action" method="post">
查询条件:
<table width="100%" border=1>
<tr>
<!-- 使用了包装类型vo后,将使用对象.xx的形式编写name属性值 -->
商品名称:<td><input type="text" name="items.name"/></td>
商品价格:<td><input type="text" name="items.price"/></td>
<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>
</tr>
<!-- 批量修改操作时,可以使用List<pojo>接收,list封装在vo中
而input中属性值为vo的相关list属性名称和下标.list泛型属性名称
也就是例如:itemsList[${status.index }].id
vo中集合为:private List<Items> itemsList;
-->
<c:forEach items="${itemList }" var="item" varStatus="status">
<tr>
<td>
<input type="checkbox" name="ids" value="${item.id }"/>
<input type="hidden" name="itemsList[${status.index }].id" value="${item.id }"/>
</td>
<td><input type="text" name="itemsList[${status.index }].name" value="${item.name }"/></td>
<td><input type="text" name="itemsList[${status.index }].price" value="${item.price }"/></td>
<td><input type="text" name="itemsList[${status.index }].createtime" value="<fmt:formatDate value='${item.createtime}' pattern='yyyy-MM-dd HH:mm:ss'/>"/></td>
<td><input type="text" name="itemsList[${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>
</body>
</html>

 1.@RequestBody

 作用:

    @RequestBody注解用于读取http请求的内容(字符串),通过springmvc提供的HttpMessageConverter接口将读到的内容转换为json、xml等格式的数据

    并绑定到controller方法的参数上。

  List.action?id=1&name=zhangsan&age=12

  本例子应用:

    @RequestBody注解实现接收http请求的json数据,将json数据转换为java对象

  可以采用打断点的形式测试以下controller是否正常接收了数据:

//测试和JSON交互
/*导入jackson包后,在controller方法中可以使用@RequestBody,
让springMVC将json格式字符串自动转换成POJO中的属性*/
@RequestMapping("/sendJson")
public String sendJson(@RequestBody Items items) throws Exception{
System.out.println(items);
return "";
}

  //能成功注入的关键是jar的支持和json的key与@RequestBody注解后POJO类的属性名一致

  2@ResponseBody
  作用:
    该注解用于将Controller的方法返回的对象,通过HttpMessageConverter接口转换为指定格式的数据如:json,xml等,

    通过Response响应给客户端

  @responsebody表示该方法的返回结果直接写入HTTP response body中
  一般在异步获取数据时使用,在使用@RequestMapping后,返回值通常解析为跳转路径,加上@responsebody后返回结果不会被解析为跳转路径,而是直接写入HTTP response body中。比如异步获取json数据,加上@responsebody后,会直接返回json数据。

【更新】:等同于原生态Servlet中response.getWriter().print()方法

  本例子应用:
    @ResponseBody注解实现将controller方法返回对象转换为json响应给客户端

  这里我们将上面的例子稍加改变:

// 商品修改提交json信息,响应json信息
@RequestMapping("/editItemSubmit_RequestJson")
public @ResponseBody Items editItemSubmit_RequestJson(@RequestBody Items items) throws Exception {
System.out.println(items);
//itemService.saveItem(items);
return items; }

  //笔记中的写法:

//测试和JSON交互
/*导入jackson包后,在controller方法中可以使用@RequestBody,
让springMVC将json格式字符串自动转换成POJO中的属性*/
@RequestMapping("/sendJson")
@ResponseBody
public Items sendJson(@RequestBody Items items) throws Exception{
System.out.println(items);
//springMVC会自动将POJO转换成key-value形式的json格式
return items;
}

  //测试的时候呢,在syso出打个断点,在前台F12进行调试看响应即可!

  可以通过@ResponseEntity<T> (byte[]) 等实现下载的功能(响应文件)

不使用注解也可以使用以下形式:

  当然也可以有其他形式:return new ResponseEntity<Course>(course,HttpStatus.ok);

 @RequestMapping("/handle")
public ResponseEntity<String> handle() {
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.set("MyResponseHeader", "MyValue");
return new ResponseEntity<String>("Hello World", responseHeaders, HttpStatus.CREATED);
}

七、springMVC的RESTful

  1.什么是RESTful?

  字面意思:资源表现层状态转化(execuse me?)——核心是资源

  Restful就是一个资源定位及资源操作的风格。不是标准也不是协议,只是一种风格,是对http协议的诠释。

  资源定位:互联网所有的事物都是资源,要求url中没有动词,只有名词。没有参数

  Url格式:http://blog.csdn.net/beat_the_world/article/details/45621673

  资源操作:使用 put、deletepost、get,使用不同方法对资源进行操作。分别对应添加、删除、修改、查询。一般使用时还是post和get。Put和Delete几乎不使用。(发送PUT DELETE请求需要配置HiddenHttpMethodFilter过滤器(web.xml中过滤器) url-pattern请 配置为/*

<!-- 配置 HiddenHttpMethodFilter: 把 POST 请求转为 DELETE、PUT 请求 -->
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter> <filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

  发送相关请求的写法可以参见(核心就是所有这些写法都是RESTful风格!)

<form action="springmvc/testRest/1" method="post">
<input type="hidden" name="_method" value="PUT"/>
<input type="submit" value="TestRest PUT"/>
</form>
<br><br> <form action="springmvc/testRest/1" method="post">
<input type="hidden" name="_method" value="DELETE"/>
<input type="submit" value="TestRest DELETE"/>
</form>
<br><br> <form action="springmvc/testRest" method="post">
<input type="submit" value="TestRest POST"/>
</form>
<br><br> <a href="springmv

 //打开源码可以看到需要配置 _method的隐藏域

   通俗理解:

    也就是我们的URL不写?传参,也不写.action这样的,要访问一个资源,以上面的URL为例,之前可以是这样

      http://blog.csdn.net/beat_the_world/article/details?id=45621673

    我们使用的是传参的形式,使用RESTful风格后就是上面的风格,只有名词,而没有动词参数之类的

  2.改造项目为RESTful风格——实战见SSM-CRUD项目

  1) 之前配置文件是在web.xml中配置的前端控制器,我们配置的是*.action

<servlet-mapping>
<servlet-name>springMVC</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>

  这里我们改造为 / ,使他符合RESTful风格:

<url-pattern>/</url-pattern>

  //也就是有 / 的都拦截,拦截所有不包括.jsp,如果是 /* ,则.jsp也会拦截

  2) 接下来我们看页面 itemlist.jsp,发现修改的超链接这里,有这么一行 ? 传参了,这里需要改:

<td><a href="${pageContext.request.contextPath }/itemEdit.action?id=${item.id}">修改</a></td>

  按照我们的原则,把.action以及传参的部分进行修改如下:

<td><a href="${pageContext.request.contextPath }/itemEdit/${item.id}">修改</a></td>

  3) 然后,对controller进行改造:

/*
* 通过@PathVariable注解可以接收URL中传过来的参数
* 其中@RequestMapping中的id和@PathVariable中的id需要一致
* */
//点击修改到详情页
@RequestMapping("/itemEdit/{id}")
public String itemEdit(@PathVariable("id") Integer id,HttpServletRequest request,HttpServletResponse response,
HttpSession session,Model model) throws Exception{
//原始的通过request拿参数
//String id = request.getParameter("id");
Items items = service.findItemsById(id);
//model叫做模型,模型中放入返回给页面的数据
//底层用的就是request域来传递数据,但是进行了拓展
model.addAttribute("item", items);
//如果springMVC方法返回一个简单的string字符串
//springMVC就是认为这就是页面的名称
return "editItem";
}

  //这里需要注意@PathVariable注解(接收占位符)接收参数的使用,以及@RequestMapping中URL的接收参数风格(使用{}进行接收)

    这是springMVC向RESTful风格发展的里程碑式意义的新功能,使用的时候和RequestMapping的一起使用完成此功能!

    (从字面意思也可以看出,Path:和路径有关,Variable变量,将路径中的变量映射过来),这样,就很容易实现RESTful风格了!

  我们测试一下改造是否成功:

    访问list页面,此时不需要加action(已经修改为/),再点击修改,我们可以看到页面的URL变为RESTful风格了

http://localhost:8080/ssm-02/itemEdit/1

  打开controller的方法,可以找到一个action跳转的代码:

return "redirect:itemEdit.action";

  按照RESTful的风格,我们把.action去掉

return "redirect:itemEdit";

  直接这样操作发现会报404,并且地址栏会自动拼凑 ?id= ,原因是我们的controller里使用了model,只要用model,数据就会自动拼进去

  所以路径不匹配,报了404

  那么我们对返回url进行如下改造:

return "redirect:itemEdit/"+items.getId();

 完整的controller如下:

package cn.controller;

import java.io.File;
import java.util.List;
import java.util.UUID; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView; import cn.pojo.Items;
import cn.service.ItemsService;
import cn.vo.QueryVo; @Controller
public class ItemsController { //注入service,使用的注解为resource;注意与autowired的区别
/*@Resource("itemsService")*
* 此方法适合多实现时指定实现类,指定名称为实现类上@service的指定类名
* AutoWired为唯一实现类的自动装配*/
@Autowired
private ItemsService service;
@RequestMapping(value="/list")
public ModelAndView itemsList() throws Exception{
List<Items> list = service.list();
//需要modelAndView
ModelAndView modelAndView = new ModelAndView();
//加模型
modelAndView.addObject("itemList", list);
//加要去的视图路径
modelAndView.setViewName("itemList");
return modelAndView;
}
/*
* 通过@PathVariable注解可以接收URL中传过来的参数
* 其中@RequestMapping中的id和@PathVariable中的id需要一致
* */
//点击修改到详情页
@RequestMapping("/itemEdit/{id}")
public String itemEdit(@PathVariable("id") Integer id,
HttpServletRequest request,HttpServletResponse response,
HttpSession session,Model model) throws Exception{
//原始的通过request拿参数
//String id = request.getParameter("id");
Items items = service.findItemsById(id);
//model叫做模型,模型中放入返回给页面的数据
//底层用的就是request域来传递数据,但是进行了拓展
model.addAttribute("item", items);
//如果springMVC方法返回一个简单的string字符串
//springMVC就是认为这就是页面的名称
return "editItem";
}
//修改页面数据的方法(记得配requestMapping)
//public String updateitem(Integer id,
//String name,Float price,String detail) throws Exception{
@RequestMapping("/updateitem")
public String updateitem(MultipartFile pictureFile,Items items, Model model) throws Exception{
//由于字段限制非空,必须加时间(后面配置转换器后不需要了)
/*items.setCreatetime(new Date());*/
service.updateitem(items);
//获取图片完整名称
String fileStr = pictureFile.getOriginalFilename();
//使用随机生成的字符串加原图片名组成新图片的名称,进而防止文件重名(UUID)
String uuid = UUID.randomUUID().toString();
String newFilename = uuid + fileStr.substring(fileStr.lastIndexOf("."));
//将图片保存到 硬盘
pictureFile.transferTo(new File("F:/tomcat_pic/"+newFilename));
//将图片名称保存到数据库
items.setPic(newFilename);
model.addAttribute("id", items.getId());
return "redirect:itemEdit/"+items.getId();
}
//查询的方法,主要用于演示绑定参数为vo等包装类型的,为简便起见采用断点查看
@RequestMapping("/serach")
public String serach(QueryVo vo) throws Exception{
System.out.println(vo);
return "";
}
//批量删除的方法
@RequestMapping("/delAll")
public String delAll(QueryVo vo) throws Exception{
//一般不会单独接收某一个参数,而是采用接收vo的形式,以便拓展等
//类似这种批量删除一堆的,可以采用只用checkbox被选中才被传递进来,并在此处使用vo里的数组接收
System.out.println(vo);
return "";
}
//批量修改的方法
@RequestMapping("/updateAll")
public String updateAll(QueryVo vo) throws Exception{
System.out.println(vo);
return "";
}
//测试和JSON交互
/*导入jackson包后,在controller方法中可以使用@RequestBody,
让springMVC将json格式字符串自动转换成POJO中的属性*/
@RequestMapping("/sendJson")
@ResponseBody
public Items sendJson(@RequestBody Items items) throws Exception{
System.out.println(items);
//springMVC会自动将POJO转换成key-value形式的json格式
return items;
}
}

   当然,我们也可以改造成接收多个参数的:

   jsp页面改为传两个参:

<td><a href="${pageContext.request.contextPath }/itemEdit/${item.id}/zhangsan">修改</a></td>

  相应的,controller也进行相应的修改:

//点击修改到详情页
@RequestMapping("/itemEdit/{id}/{name}")
public String itemEdit(@PathVariable("id") Integer id,@PathVariable("name") String name,
HttpServletRequest request,HttpServletResponse response,
HttpSession session,Model model) throws Exception{

  接下来对其他需要改造的地方进行改造,我们看itemlist.jsp中只有一个form提交处有URL,把.action去掉就可以了:

<form action="${pageContext.request.contextPath }/updateAll" method="post">

  5.静态资源访问<mvc:resources>

  如果在DispatcherServlet中设置url-pattern为 /则必须对静态资源进行访问处理。

  spring mvc 的<mvc:resources mapping="" location="">实现对静态资源进行映射访问。

  如下是对js文件访问配置:

  <mvc:resources location="/js/" mapping="/js/**"/>

  静态资源访问day01亦有相关介绍

八、拦截器 

  什么是拦截器?

  Spring Web MVC 的处理器拦截器类似于Servlet 开发中的过滤器Filter,用于对处理器进行预处理和后处理

  定义拦截器

  我们通过实现springMVC提供的 HandlerInterceptor 接口来定义拦截器

  (拦截器需实现三个方法,通过方法名也容易分辨三个方法的执行顺序)

  配置拦截器

  在springMVC.xml中进行配置

<!-- 配置拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<!-- 拦截器请求路径 ,要拦截所有必须配置成/**,直接/*是拦截一层目录,
这里是拦截所有,故需要/**-->
<mvc:mapping path="/**"/>
<!-- 拦截器指定位置 -->
<bean class="cn.interceptor.Interceptor01"></bean>
</mvc:interceptor>
</mvc:interceptors>

  //具体格式不再赘述,见注释,需要注意的是path需要配置为 /**

  拦截器如下:

package cn.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView; public class Interceptor01 implements HandlerInterceptor { //在拦截后执行
//执行时机:controller方法已经执行,modelAndView已经返回
//用于操作日志,用户登录IP和时间等
@Override
public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
throws Exception { } //在拦截时执行
//执行时机:controller方法已经执行,modelAndView没有返回
//用于全局的都需要的数据处理
@Override
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
throws Exception { }
//返回true则放行,返回false则拦截
//执行时机:controller方法没有执行,modelAndView还没有返回
//用于权限验证,在执行之前的一些验证等(例如验证session等)
@Override
public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
return true;
} }

  //这里用法等见注释

  上面的是针对所有mapping,针对某种Mapping这里不展开,给出参考配置:

<bean
class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
<property name="interceptors">
<list>
<ref bean="handlerInterceptor1"/>
<ref bean="handlerInterceptor2"/>
</list>
</property>
</bean>
<bean id="handlerInterceptor1" class="springmvc.intercapter.HandlerInterceptor1"/>
<bean id="handlerInterceptor2" class="springmvc.intercapter.HandlerInterceptor2"/>

  *如果有多个拦截器,则会按照配置文件配置的顺序进行顺序执行

  执行顺序:(pre正序,后两个反序,打断点看循环的索引可以验证 i=0.i++,后面的i=length,i--)

HandlerInterceptor1..preHandle..
HandlerInterceptor2..preHandle.. HandlerInterceptor2..postHandle..
HandlerInterceptor1..postHandle.. HandlerInterceptor2..afterCompletion..
HandlerInterceptor1..afterCompletion..

  典型应用:登陆权限验证

  流程:

  1)编写登录的controller, 编写跳转到登录页面的方法, 编写登录验证方法
  2)编写登录页面
  3)编写拦截器
  运行过程:
    1)访问随意一个页面,拦截器会拦截请求,会验证session中是否有登录信息
      如果已登录,放行
      如果未登录,跳转到登录页面
    2)在登录页面中输入用户名,密码,点击登录按钮,拦截器会拦截请求,如果是登录路径放行
      在controller方法中判断用户名密码是否正确,如果正确则将登录信息放入session

  1.分析完成后我们来实现一下;没有登陆页面,我们先编写一个登陆页面:

  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>Insert title here</title>
</head>
<body>
<form action="" method="POST">
<table>
<tr>
<td>
<input name="username" type="text"/>
</td>
</tr>
<tr>
<td>
<input name="password" type="password"/>
</td>
</tr>
<tr>
<td>
<input value="登陆" type="submit"/>
</td>
</tr>
</table>
</form>
</body>
</html>

  //这里的action,待后续补充

  2.我们再新建一个controller,(注意把注解等加上)

package cn.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping; @Controller
@RequestMapping("/login")
public class LoginController { @RequestMapping("/login")
public String login() throws Exception{
//跳转到登陆页面
return "login";
}
//接收参数采用普通类型,和表单name属性一致即可
//还可以接收默认类型,直接拿到session
@RequestMapping("/sumbit")
public String sumbit(String username,String password,HttpServletRequest request) throws Exception{
//验证这里不调service dao ,实际时可以适当改动
HttpSession session = request.getSession();
if("admin".equals(username) && "123".equals(password)){
session.setAttribute("username", username);
}
return "redirect:/list";
}
}

  3.把拦截器LoginInterceptor完成

package cn.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView; public class LoginInterceptor implements HandlerInterceptor { @Override
public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
throws Exception {
// TODO Auto-generated method stub } @Override
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
throws Exception {
// TODO Auto-generated method stub } @Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception {
//判断当然访问路径是否是登陆
if(request.getRequestURI().indexOf("/login") > 0){
return true;
}
//判断session中是否有登陆信息,有则放行
if(request.getSession().getAttribute("username") != null){
return true;
}
request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
return false;
} }

  拦截器完之后将拦截器注册一下:

<!-- 配置拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<!-- 拦截器请求路径 ,要拦截所有必须配置成/**,直接/*是拦截一层目录,
这里是拦截所有,故需要/**-->
<mvc:mapping path="/**"/>
<!-- 拦截器指定位置 -->
<bean class="cn.interceptor.LoginInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>

  把前面Login页面的action可以完善了:

<form action="${pageContext.request.contextPath }/login/sumbit" method="POST">

  测试时可以通过打断点进行测试

 小结:

总结:
☆1.参数绑定:(从请求中接收参数)
1)默认支持的类型:Request,Response,Session,Model
2)基本数据类型(包含String)
3)Pojo类型
4)Vo类型
5)Converter自定义转换器
6)数组
7)List ☆2.controller方法返回值(指定返回到哪个页面, 指定返回到页面的数据)
1)ModelAndView
modelAndView.addObject("itemList", list); 指定返回页面的数据
modelAndView.setViewName("itemList"); 指定返回的页面
2)String(推荐使用)
返回普通字符串,就是页面去掉扩展名的名称, 返回给页面数据通过Model来完成
返回的字符串以forward:开头为请求转发
返回的字符串以redirect:开头为重定向
3)返回void(使用它破坏了springMvc的结构,所以不建议使用)
可以使用request.setAttribut 来给页面返回数据
可以使用request.getRquestDispatcher().forward()来指定返回的页面
如果controller返回值为void则不走springMvc的组件,所以要写页面的完整路径名称 相对路径:相对于当前目录,也就是在当前类的目录下,这时候可以使用相对路径跳转
绝对路径:从项目名后开始.
在springMvc中不管是forward还是redirect后面凡是以/开头的为绝对路径,不以/开头的为相对路径
例如:forward:/items/itemEdit.action 为绝对路径
forward:itemEdit.action为相对路径 3.架构级别异常处理:
主要为了防止项目上线后给用户抛500等异常信息,所以需要在架构级别上整体处理.hold住异常
首先自定义全局异常处理器实现HandlerExceptionResolver接口
在spirngMvc.xml中配置生效
4.上传图片:
1)在tomcat中配置虚拟图片服务器
2)导入fileupload的jar包
3)在springMvc.xml中配置上传组件
4)在页面上编写上传域,更改form标签的类型
5)在controller方法中可以使用MultiPartFile接口接收上传的图片
6)将文件名保存到数据库,将图片保存到磁盘中
5.Json数据交互:
需要加入jackson的jar包
@Requestbody:将页面传到controller中的json格式字符串自动转换成java的pojo对象
@ResponseBody:将java中pojo对象自动转换成json格式字符串返回给页面
6.RestFul支持:
就是对url的命名标准,要求url中只有能名词,没有动词(不严格要求),但是要求url中不能用问号?传参
传参数:
页面:${pageContext.request.contextPath }/items/itemEdit/${item.id}
方法: @RquestMapping("/itemEdit/{id}")
方法: @PathVariable("id") Integer idd
7.拦截器:
作用:拦截请求,一般做登录权限验证时用的比较多
1)需要编写自定义拦截器类,实现HandlerInterceptor接口
2)在spirngMvc.xml中配置拦截器生效 8.登录权限验证:
1)编写登录的controller, 编写跳转到登录页面的方法, 编写登录验证方法
2)编写登录页面
3)编写拦截器
运行过程:
1)访问随意一个页面,拦截器会拦截请求,会验证session中是否有登录信息
如果已登录,放行
如果未登录,跳转到登录页面
2)在登录页面中输入用户名,密码,点击登录按钮,拦截器会拦截请求,如果是登录路径放行
在controller方法中判断用户名密码是否正确,如果正确则将登录信息放入session

  国际化需要国际化资源文件,配置文件配置,页面使用<fmt message key="">进行使用【具体待更新】

  springMVC常用注解http://www.cnblogs.com/leskang/p/5445698.html

springMVC第二天——高级参数绑定与其它特性的更多相关文章

  1. JAVAEE——SpringMVC第二天:高级参数绑定、@RequestMapping、方法返回值、异常处理、图片上传、Json交互、实现RESTful、拦截器

    1. 课前回顾 https://www.cnblogs.com/xieyupeng/p/9093661.html 2. 课程计划 1.高级参数绑定 a) 数组类型的参数绑定 b) List类型的绑定 ...

  2. SpringMVC学习笔记之二(SpringMVC高级参数绑定)

    一.高级参数绑定 1.1 绑定数组 需求:在商品列表页面选中多个商品,然后删除. 需求分析:功能要求商品列表页面中的每个商品前有一个checkbok,选中多个商品后点击删除按钮把商品id传递给Cont ...

  3. (转)SpringMVC学习(六)——SpringMVC高级参数绑定与@RequestMapping注解

    http://blog.csdn.net/yerenyuan_pku/article/details/72511749 高级参数绑定 现在进入SpringMVC高级参数绑定的学习,本文所有案例代码的编 ...

  4. JAVAEE——SpringMVC第一天:介绍、入门程序、架构讲解、SpringMVC整合MyBatis、参数绑定、SpringMVC和Struts2的区别

    1. 学习计划   第一天 1.SpringMVC介绍 2.入门程序 3.SpringMVC架构讲解 a) 框架结构 b) 组件说明 4.SpringMVC整合MyBatis 5.参数绑定 a) Sp ...

  5. SSM框架之SpringMVC(2)参数绑定及自定义类型转换

    SpringMVC(2)参数绑定及自定义类型转换 1.请求参数的绑定 1.1. 请求参数的绑定说明 1.1.1.绑定机制 表单提交的数据都是k=v格式的 username=haha&passw ...

  6. SpringMVC高级参数绑定(数组和List)

    本节内容: 参数绑定之数组 将表单的数据绑定到List 复制下上篇博客中的工程,作为今天开发的工程. 一.参数绑定之数组 1. 需求 在商品列表页面选中多个商品,然后删除. 2. 需求分析 功能要求商 ...

  7. SpringMVC框架笔记02_参数绑定返回值文件上传异常处理器JSON数据交互_拦截器

    目录 第1章 高级参数的绑定 1.1 参数的分类 1.2 数组类型的参数的绑定 1.3 集合类型的参数的绑定 第2章 @RequestMapping的用法 2.1 URL路径映射 2.2 请求方法限定 ...

  8. SpringMVC之Controller和参数绑定

    在上一篇Spring+SpringMVC+Mybatis整合中说到了SSM的整合,并且在其中添加了一个简单的查询功能,目的只是将整个整合的流程进行一个梳理,下面在上一篇中工程的基础上再说一些关于Spr ...

  9. SpringMVC框架三:参数绑定

    这篇文章整合了SpringMVC和MyBatis: https://www.cnblogs.com/xuyiqing/p/9419144.html 接下来看看参数绑定: 默认Conrtroller可以 ...

随机推荐

  1. Chapter 5 Blood Type——11

    "I just wondered… if you could warn me beforehand the next time you decide to ignore me for my ...

  2. Asp.Net SignalR Hub类中的操作详解

    Hub类中的操作 在服务端我们要通过Hub类做一系列操作,下面就说说我们都可以做什么操作 客户端的发送消息操作 调用所有的客户端的helloClient方法 Clients.All.helloClie ...

  3. Asp.Net Core 程序部署到Linux(centos)生产环境(一):普通部署

    运行环境 照例,先亮底 centos:7.2 cpu:1核 2G内存 1M带宽 辅助工具:xshell xftp 搭建.net core运行环境 .net core 的运行环境我单独写了一篇,请看我的 ...

  4. springmvc 项目完整示例02 项目创建-eclipse创建动态web项目 配置文件 junit单元测试

    包结构 所需要的jar包直接拷贝到lib目录下 然后选定 build path 之后开始写项目代码 配置文件 ApplicationContext.xml <?xml version=" ...

  5. springmvc 项目完整示例07 设置配置整合springmvc springmvc所需jar包springmvc web.xml文件配置

    前面主要是后台代码,spring以及mybatis的整合 下面主要是springmvc用来处理请求转发,展现层的处理 之前所有做到的,完成了后台,业务层和持久层的开发完成了 接下来就是展现层了 有很多 ...

  6. 页面优化,DocumentFragment对象详解

    一.前言 最近项目不是很忙,所以去看了下之前总想整理的重汇和回流的相关资料,关于回流优化,提到了DocumentFragment的使用,这个对象在3年前我记得是有看过的,但是一直没深入了解过,所以这里 ...

  7. vue + elementUi + upLoadIamge组件 上传文件到阿里云oss

    <template> <div class="upLoadIamge"> <el-upload action="https://jsonpl ...

  8. CSS3图片翻转动画技术详解

    CSS动画非常的有趣:这种技术的美就在于,通过使用很多简单的属性,你能创建出漂亮的消隐效果.其中代表性的一种就是CSS图片翻转效果,能让你看到一张卡片的正反两面上的内容.本文就是要用最简单的方法向大家 ...

  9. python地理处理包——pySAL使用

    Pysal是基于Python的开源地理处理库,能提供高层次的空间分析功能.

  10. OpenCL:图像处理基础note

    使用图像对象的理由 虽然对于图像也可以把它的像素数据当做一般的缓存数据来处理,但是如果把它当做图像来处理有如下好处: 在GPU中,图像数据是保存在特殊的全局内存中,即纹理内存,它和一般的全局内存不相同 ...