SpringMVC框架基础知识总结(二)

SpringMVC框架基础知识总结(二)

一、集合类型绑定

1.1 数组绑定

关键:将页面选择(多选)的商品id,传到controller方法的形参,方法形参使用数组接收页面请求的多个商品id。
controller方法定义:

// 批量删除 商品信息
@RequestMapping("/deleteItems")
public String deleteItems(Integer[] items_id) throws Exception {

    // 调用service批量删除商品
    // ...

    return "success";

}

1.2 list绑定

  • 1.2.1 需求
    通常在需要批量提交数据时,将提交的数据绑定到list中,比如:成绩录入(录入多门课成绩,批量提交),本例子需求:批量商品修改,在页面输入多个商品信息,将多个商品信息提交到controller方法中。
  • 1.2.2 表现层实现

    • controller方法定义:
      1、进入批量商品修改页面(页面样式参考商品列表实现)
      2、批量修改商品提交,使用List接收页面提交的批量数据,通过包装pojo接收,在包装pojo中定义list属性

      public class ItemsQueryVo {
      
          //商品信息
          private Items items;
      
          //为了系统的可扩展性,对原始的po进行扩展
          private ItemsCustom itemsCustom;
      
      
          //批量商品信息
          private List<ItemsCustom> itemsList;
      // 批量修改商品页面,将商品信息查询出来,在页面中可以编辑商品信息
          @RequestMapping("/editItemsQuery")
          public ModelAndView editItemsQuery(HttpServletRequest request,
                  ItemsQueryVo itemsQueryVo) throws Exception {

      页面定义:
      这里写图片描述

1.3 map绑定

也通过在包装pojo中定义map类型属性。在包装类中定义Map对象,并添加get/set方法,action使用包装对象接收。包装类中定义Map对象如下:

Public class QueryVo {
private Map<String, Object> itemInfo = new HashMap<String, Object>();
  //get/set方法..
}

页面定义如下:

<tr>
<td>学生信息:</td>
<td>
姓名:<inputtype="text"name="itemInfo['name']"/>
年龄:<inputtype="text"name="itemInfo['price']"/>
.. .. ..
</td>
</tr>

Contrller方法定义如下:

public String useraddsubmit(Model model,QueryVo queryVo)throws Exception{
System.out.println(queryVo.getStudentinfo());
}

二、springmvc校验

2.1 校验理解

项目中,通常使用较多是前端的校验,比如页面中js校验。对于安全要求较高点建议在服务端进行校验。

  • 服务端校验:
    • 控制层conroller:校验页面请求的参数的合法性。在服务端控制层conroller校验,不区分客户端类型(浏览器、手机客户端、远程调用)
    • 业务层service(使用较多):主要校验关键业务参数,仅限于service接口中使用的参数。
    • 持久层dao:一般是不校验的。

2.2 springmvc校验需求

springmvc使用hibernate的校验框架validation(和hibernate没有任何关系)。

  • 校验思路:
    页面提交请求的参数,请求到controller方法中,使用validation进行校验。如果校验出错,将错误信息展示到页面。
  • 具体需求:
    商品修改,添加校验(校验商品名称长度,生产日期的非空校验),如果校验出错,在商品修改页面显示错误信息。

2.3 环境准备

hibernate的校验框架validation所需要jar包:
这里写图片描述

2.4 配置校验器

<!-- 校验器 -->
<bean id="validator"
    class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
    <!-- hibernate校验器-->
    <property name="providerClass" value="org.hibernate.validator.HibernateValidator" />
    <!-- 指定校验使用的资源文件,在文件中配置校验错误信息,如果不指定则默认使用classpath下的ValidationMessages.properties -->
    <property name="validationMessageSource" ref="messageSource" />
</bean>


<!-- 校验错误信息配置文件 -->
<bean id="messageSource"
    class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
    <!-- 资源文件名-->
    <property name="basenames">   
         <list>    
           <value>classpath:CustomValidationMessages</value> 
         </list>   
    </property>
    <!-- 资源文件编码格式 -->
    <property name="fileEncodings" value="utf-8" />
    <!-- 对资源文件内容缓存时间,单位秒 -->
    <property name="cacheSeconds" value="120" />
</bean>

2.5 校验器注入到处理器适配器中

<!-- 使用 mvc:annotation-driven代替上边注解映射器和注解适配器配置
mvc:annotation-driven默认加载很多的参数绑定方法,
比如json转换解析器就默认加载了,如果使用mvc:annotation-driven不用配置上边的RequestMappingHandlerMapping和RequestMappingHandlerAdapter
实际开发时使用mvc:annotation-driven
 -->
<mvc:annotation-driven conversion-service="conversionService"
validator="validator"></mvc:annotation-driven>

2.6 在pojo中添加校验规则

   //校验名称在1到30字符中间
   //message是提示校验出错显示的信息
   //groups:此校验属于哪个分组,groups可以定义多个分组
   @Size(min=1,max=30,message="{items.name.length.error}",groups={ValidGroup1.class} )
   private String name;

   private Float price;

   private String pic;

   //非空校验
   @NotNull(message="{items.createtime.isNUll}")
   private Date createtime;

2.7 CustomValidationMessages.properties

在CustomValidationMessages.properties配置校验错误信息:

items.name.length.error=请输入130个字符的商品名称
items.createtime.isNUll=请输入商品的生产日期

2.8 捕获校验错误信息

// 商品信息修改提交
// 在需要校验的pojo前边添加@Validated,在需要校验的pojo后边添加BindingResult
// bindingResult接收校验出错信息
// 注意:@Validated和BindingResult bindingResult是配对出现,并且形参顺序是固定的(一前一后)。
// value={ValidGroup1.class}指定使用ValidGroup1分组的 校验
// @ModelAttribute可以指定pojo回显到页面在request中的key
@RequestMapping("/editItemsSubmit")
public String editItemsSubmit(
        Model model,
        HttpServletRequest request,
        Integer id,
        @ModelAttribute("items") @Validated(value={ValidGroup1.class}) ItemsCustom itemsCustom,
        BindingResult bindingResult,
        MultipartFile items_pic//接受商品图片
        ) throws Exception {
  • 在需要校验的pojo前边添加@Validated,在需要校验的pojo后边添加BindingResult bindingResult接收校验出错信息
  • 注意:@Validated和BindingResult bindingResult是配对出现,并且形参顺序是固定的(一前一后)。

2.9 在页面显示校验错误信息

在controller中将错误信息传到页面即可。

// 商品信息修改提交
// 在需要校验的pojo前边添加@Validated,在需要校验的pojo后边添加BindingResult
// bindingResult接收校验出错信息
// 注意:@Validated和BindingResult bindingResult是配对出现,并且形参顺序是固定的(一前一后)。
// value={ValidGroup1.class}指定使用ValidGroup1分组的 校验
// @ModelAttribute可以指定pojo回显到页面在request中的key
@RequestMapping("/editItemsSubmit")
public String editItemsSubmit(
        Model model,
        HttpServletRequest request,
        Integer id,
        @ModelAttribute("items") @Validated(value={ValidGroup1.class}) ItemsCustom itemsCustom,
        BindingResult bindingResult,
        MultipartFile items_pic//接受商品图片
        ) throws Exception {

    // 获取校验错误信息
    if (bindingResult.hasErrors()) {
        // 输出错误信息
        List<ObjectError> allErrors = bindingResult.getAllErrors();

        for (ObjectError objectError : allErrors) {
            // 输出错误信息
            System.out.println(objectError.getDefaultMessage());

        }
        // 将错误信息传到页面
        model.addAttribute("allErrors", allErrors);


        //可以直接使用model将提交pojo回显到页面使用@ModelAttribute("items")这种方法也可以
        model.addAttribute("items", itemsCustom);

        // 出错重新到商品修改页面
        return "items/editItems";
    }

页面显示错误信息:

<!-- 显示错误信息 -->
<c:if test="${allErrors!=null }">
    <c:forEach items="${allErrors }" var="error">
    ${ error.defaultMessage}<br />
    </c:forEach>
</c:if>

2.10 分组校验

  • 2.10.1 需求
    在pojo中定义校验规则,而pojo是被多个 controller所共用,当不同的controller方法对同一个pojo进行校验,但是每个controller方法需要不同的校验。
    • 解决方法:
      定义多个校验分组(其实是一个java接口),分组中定义有哪些规则
      每个controller方法使用不同的校验分组
  • 2.10.2 校验分组

    public interface ValidGroup1 {
        //接口中不需要定义任何方法,仅是对不同的校验规则进行分组
        //此分组只校验商品名称长度
    
    }
  • 2.10.3 在校验规则中添加分组

    public class Items {
        private Integer id;
    
        //校验名称在1到30字符中间
        //message是提示校验出错显示的信息
        //groups:此校验属于哪个分组,groups可以定义多个分组
        @Size(min=1,max=30,message="{items.name.length.error}",groups={ValidGroup1.class} )
        private String name;
    
        private Float price;
    
        private String pic;
    
        //非空校验
        @NotNull(message="{items.createtime.isNUll}")
        private Date createtime;
  • 2.10.4 在controller方法使用指定分组的校验

    // 商品信息修改提交
    // 在需要校验的pojo前边添加@Validated,在需要校验的pojo后边添加BindingResult
    // bindingResult接收校验出错信息
    // 注意:@Validated和BindingResult bindingResult是配对出现,并且形参顺序是固定的(一前一后)。
    // value={ValidGroup1.class}指定使用ValidGroup1分组的 校验
    // @ModelAttribute可以指定pojo回显到页面在request中的key
    @RequestMapping("/editItemsSubmit")
    public String editItemsSubmit(
            Model model,
            HttpServletRequest request,
            Integer id,
            @ModelAttribute("items") @Validated(value={ValidGroup1.class}) ItemsCustom itemsCustom,
            BindingResult bindingResult,
            MultipartFile items_pic//接受商品图片
            ) throws Exception {

三、数据回显

3.1 什么数据回显

提交后,如果出现错误,将刚才提交的数据回显到刚才的提交页面。

3.2 pojo数据回显方法

  • 1、springmvc默认对pojo数据进行回显。
    pojo数据传入controller方法后,springmvc自动将pojo数据放到request域,key等于pojo类型(首字母小写)使用@ModelAttribute指定pojo回显到页面在request中的key
  • 2、@ModelAttribute还可以将方法的返回值传到页面在商品查询列表页面,通过商品类型查询商品信息。在controller中定义商品类型查询方法,最终将商品类型传到页面。

    // 商品分类
    //itemtypes表示最终将方法返回值放在request中的key
    @ModelAttribute("itemtypes")
    public Map<String, String> getItemTypes() {
    
        Map<String, String> itemTypes = new HashMap<String, String>();
        itemTypes.put("101", "数码");
        itemTypes.put("102", "母婴");
    
        return itemTypes;
    }

    页面上可以得到itemTypes数据。

    商品类型:
    <select name="itemtype">
        <c:forEach items="${itemtypes }" var="itemtype">
            <option value="${itemtype.key }">${itemtype.value }</option>      
        </c:forEach>
    </select>
  • 3、 使用最简单方法使用model,可以不用@ModelAttribute

    //可以直接使用model将提交pojo回显到页面使用@ModelAttribute("items")这种方法也可以
    model.addAttribute("items", itemsCustom);

四、异常处理

4.1 异常处理思路

系统中异常包括两类:预期异常和运行时异常RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。
系统的dao、service、controller出现都通过throws Exception向上抛出,最后由springmvc前端控制器交由异常处理器进行异常处理,如下图:
这里写图片描述
springmvc提供全局异常处理器(一个系统只有一个异常处理器)进行统一异常处理。

4.2 自定义异常类

对不同的异常类型定义异常类,继承Exception。

public class CustomException extends Exception {

    //异常信息
    public String message;

    public CustomException(String message){
        super(message);
        this.message = message;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

}

4.3 全局异常处理器

  • 思路:

    • 系统遇到异常,在程序中手动抛出,dao抛给service、service给controller、controller抛给前端控制器,前端控制器调用全局异常处理器。
    • 全局异常处理器处理思路:
      • 解析出异常类型
      • 如果该 异常类型是系统 自定义的异常,直接取出异常信息,在错误页面展示
      • 如果该 异常类型不是系统 自定义的异常,构造一个自定义的异常类型(信息为“未知错误”)
  • springmvc提供一个HandlerExceptionResolver接口

    /**
     * 
     * <p>Title: CustomExceptionResolver.java</p>
     * <p>Description:全局异常处理  </p>
     * <p>Company: www.wyd.com</p>
     * @author Wyd
     * @date 2017-11-29
     * @version 1.0
     */
    
    public class CustomExceptionResolver implements HandlerExceptionResolver {
    
        /**
         * (非 Javadoc)
         * <p>Title: resolveException</p>
         * <p>Description: </p>
         * @param request
         * @param response
         * @param handler
         * @param ex 系统 抛出的异常
         * @return
         * @see org.springframework.web.servlet.HandlerExceptionResolver#resolveException(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object, java.lang.Exception)
         */
        @Override
        public ModelAndView resolveException(HttpServletRequest request,
                HttpServletResponse response, Object handler, Exception ex) {
            //handler就是处理器适配器要执行Handler对象(只有method)
    
    //      解析出异常类型
    //      如果该 异常类型是系统 自定义的异常,直接取出异常信息,在错误页面展示
    //      String message = null;
    //      if(ex instanceof CustomException){
    //          message = ((CustomException)ex).getMessage();
    //      }else{
    ////            如果该 异常类型不是系统 自定义的异常,构造一个自定义的异常类型(信息为“未知错误”)
    //          message="未知错误";
    //      }
    
            //上边代码变为
            CustomException customException = null;
            if(ex instanceof CustomException){
                customException = (CustomException)ex;
            }else{
                customException = new CustomException("未知错误");
            }
    
            //错误信息
            String message = customException.getMessage();
    
    
            ModelAndView modelAndView = new ModelAndView();
    
            //将错误信息传到页面
            modelAndView.addObject("message", message);
    
            //指向错误页面
            modelAndView.setViewName("error");
    
    
            return modelAndView;
        }
    
    }

4.4 错误页面

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>错误提示</title>
</head>
<body>${message }
</body>
</html>

4.5 在springmvc.xml配置全局异常处理器


<!-- 全局异常处理器
只要实现HandlerExceptionResolver接口就是全局异常处理器
 -->
<bean class="cn.wyd.ssm.exception.CustomExceptionResolver"></bean>

4.6 异常测试

在controller、service、dao中任意一处需要手动抛出异常。如果是程序中手动抛出的异常,在错误页面中显示自定义的异常信息,如果不是手动抛出异常说明是一个运行时异常,在错误页面只显示“未知错误”。在商品修改的controller方法中抛出异常 .

@RequestMapping(value="/editItems",method={RequestMethod.POST,RequestMethod.GET})
//@RequestParam里边指定request传入参数名称和形参进行绑定。
//通过required属性指定参数是否必须要传入
//通过defaultValue可以设置默认值,如果id参数没有传入,将默认值和形参绑定。
public String editItems(Model model,@RequestParam(value="id",required=true) Integer items_id)throws Exception {

    //调用service根据商品id查询商品信息
    ItemsCustom itemsCustom = itemsService.findItemsById(items_id);

    //判断商品是否为空,根据id没有查询到商品,抛出异常,提示用户商品信息不存 在
    if(itemsCustom == null){
        throw new CustomException("修改的商品信息不存在!");
    }

    //通过形参中的model将model数据传到页面
    //相当于modelAndView.addObject方法
    model.addAttribute("items", itemsCustom);

    return "items/editItems";
}

在service接口中抛出异常:

@Override
public ItemsCustom findItemsById(Integer id) throws Exception {
    Items items = itemsMapper.selectByPrimaryKey(id);
    if(items==null){
        throw new CustomException("修改的商品信息不存在!");
    }

如果与业务功能相关的异常,建议在service中抛出异常。与业务功能没有关系的异常,建议在controller中抛出。上边的功能,建议在service中抛出异常。

五、上传图片

5.1 需求

在修改商品页面,添加上传商品图片功能。

5.2 springmvc中对多部件类型解析

在页面form中提交enctype=”multipart/form-data”的数据时,需要springmvc对multipart类型的数据进行解析。
在springmvc.xml中配置multipart类型解析器。

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

5.3 加入上传图片的jar

上边的解析内部使用下边的jar进行图片上传。
这里写图片描述

5.4 创建图片虚拟 目录 存储图片

通过图形界面配置:
这里写图片描述

也可以直接修改tomcat的配置:在conf/server.xml文件,添加虚拟目录
这里写图片描述

5.5 上传图片代码

  • 页面

    <tr>
        <td>商品图片</td>
        <td>
            <c:if test="${items.pic !=null}">
            <img src="/pic/${items.pic}" width=100 height=100/>
            <br/>
            </c:if>
            <input type="file"  name="items_pic"/> 
        </td>
    </tr> 
  • controller方法
    修改:商品修改controller方法:

    // 商品信息修改提交
    // 在需要校验的pojo前边添加@Validated,在需要校验的pojo后边添加BindingResult
    // bindingResult接收校验出错信息
    // 注意:@Validated和BindingResult bindingResult是配对出现,并且形参顺序是固定的(一前一后)。
    // value={ValidGroup1.class}指定使用ValidGroup1分组的 校验
    // @ModelAttribute可以指定pojo回显到页面在request中的key
    @RequestMapping("/editItemsSubmit")
    public String editItemsSubmit(
            Model model,
            HttpServletRequest request,
            Integer id,
            @ModelAttribute("items") @Validated(value={ValidGroup1.class}) ItemsCustom itemsCustom,
            BindingResult bindingResult,
            MultipartFile items_pic//接受商品图片
            ) throws Exception {
    //原始名称
    String originalFilename = items_pic.getOriginalFilename();
    //上传图片
    if(items_pic!=null && originalFilename!=null && originalFilename.length()>0){
    
        //存储图片的物理路径
        String pic_path = "G:\\Wyd\\web\\图片上传存放位置\\";
    
    
        //新的图片名称
        String newFileName = UUID.randomUUID() + originalFilename.substring(originalFilename.lastIndexOf("."));
    //          String newFileName = "王耀东" + originalFilename.substring(originalFilename.lastIndexOf("."));
        //新图片
        File newFile = new File(pic_path+newFileName);
    
        //将内存中的数据写入磁盘
        items_pic.transferTo(newFile);
    
        //将新图片名称写到itemsCustom中
        itemsCustom.setPic(newFileName);
    
    }

六、json交互测试

6.1 为什么要进行json数据交互

json数据格式在接口调用中、html页面中较常用,json格式比较简单,解析还比较方便。
比如:webservice接口,传输json数据.

6.2 springmvc进行json交互

这里写图片描述

  • 1、请求json、输出json,要求请求的是json串,所以在前端页面中需要将请求的内容转成json,不太方便。
  • 2、请求key/value、输出json。此方法比较常用。

6.3 环境准备

  • 6.3.1 加载json转的jar包
    springmvc中使用jackson的包进行json转换(@requestBody和@responseBody使用下边的包进行json转),如下:
    这里写图片描述
  • 6.3.2 配置json转换器
    在注解适配器中加入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>
    

    注意:如果使用 则不用定义上边的内容。

6.4 json交互测试

  • 6.4.1 输入json串,输出是json串

    • jsp页面:使用jquery的ajax提交json串,对输出的json结果进行解析。

      //请求json响应json
      function request_json(){
          $.ajax({
              type:"post",
              url:"${pageContext.request.contextPath }/item/editItemSubmit_RequestJson.action",
              contentType:"application/json;charset=utf-8",
              data:'{"name":"测试商品","price":99.9}',
              success:function(data){
                  alert(data);
              }
          });
      }
      
    • controller

      // 商品修改提交json信息,响应json信息
      //@ResponseBody 将请求的商品信息的json串转化成itemsCustom对象
      //@RequestBody将itemsCustom对象转化成json输出
      @RequestMapping("/editItemSubmit_RequestJson")
      public @ResponseBody Items editItemSubmit_RequestJson(@RequestBody Items items) throws Exception {
          System.out.println(items);
          //itemService.saveItem(items);
          return items;
      
      }
      
  • 6.4.2 输入key/value,输出是json串
    表单默认请求application/x-www-form-urlencoded格式的数据即key/value,通常有post和get两种方法,响应json数据是为了方便客户端处理,实现如下:

    • jsp页面

      function formsubmit(){
          var user = " name=测试商品&price=99.9";
          alert(user);
            $.ajax(
              {
                  type:'post',//这里改为get也可以正常执行
                  url:'${pageContext.request.contextPath}/item/ editItemSubmit_RequestJson.action',
      //ContentType没指定将默认为:application/x-www-form-urlencoded
                  data:user,
                  success:function(data){
                      alert(data.name);
                  }
      
              }   
          )
      }
      

      从上边的js代码看出,已去掉ContentType的定义,ContentType默认为:application/x-www-form-urlencoded格式。

    • controller编写
          // 商品修改提交,提交普通form表单数据,响应json
          @RequestMapping("/editItemSubmit_ResponseJson")
          public @ResponseBody Items editItemSubmit_ResponseJson(Items items) throws Exception {
      
              System.out.println(items);
      
      //      itemService.saveItem(items);
              return items;
          }
      

七、RESTful支持

7.1 什么是RESTful

RESTful架构,就是目前最流行的一种互联网软件架构。它结构清晰、符合标准、易于理解、扩展方便,所以正得到越来越多网站的采用。
RESTful(即Representational State Transfer的缩写)其实是一个开发理念,是对http的很好的诠释。

7.2 REST的例子

查询商品信息,返回json数据。

  • 7.2.2 controller
    定义方法,进行url映射使用REST风格的url,将查询商品信息的id传入controller .输出json使用@ResponseBody将java对象输出json。

    @RequestMapping("/viewItems/{id}") 
        public @ResponseBody viewItems(@PathVariable("id") String id,Model model) throws Exception{
            //方法中使用@PathVariable获取useried的值,使用model传回页面
            //调用 service查询商品信息
            ItemsCustom itemsCustom = itemsService.findItemsById(id);
            return itemsCustom;
    }
    

    @RequestMapping(value=”/ itemsView/{id}”):{×××}占位符,请求的URL可以是“/viewItems/1”或“/viewItems/2”,通过在方法中使用@PathVariable获取{×××}中的×××变量。
    @PathVariable用于将请求URL中的模板变量映射到功能处理方法的参数上。
    如果RequestMapping中表示为”/ itemsView /{id}”,id和形参名称一致,@PathVariable不用指定名称。

  • 7.2.3 REST方法的前端控制器配置
    在web.xml配置:

    <servlet>
        <servlet-name>springmvc-servlet-rest</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring/springmvc.xml</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc-servlet-rest</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    

7.3 对静态资源的解析

配置前端控制器的url-parttern中指定/,对静态资源的解析出现问题,在springmvc.xml中添加静态资源解析方法。

<!-- 静态资源解析
包括 :js、css、img、..
 -->
<mvc:resources location="/js/" mapping="/js/**"/>
<mvc:resources location="/img/" mapping="/img/**"/>

八、拦截器

8.1 拦截定义

定义拦截器,实现HandlerInterceptor接口。接口中提供三个方法。

/**
 * 
 * <p>Title: HandlerInterceptor1.java</p>
 * <p>Description: 测试拦截器1 </p>
 * <p>Company: www.wyd.com</p>
 * @author Wyd
 * @date 2017年11月29日
 * @version 1.0
 */
public class HandlerInterceptor1 implements HandlerInterceptor {


    //进入 Handler方法之前执行
    //用于身份认证、身份授权
    //比如身份认证,如果认证通过表示当前用户没有登陆,需要此方法拦截不再向下执行
    @Override
    public boolean preHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler) throws Exception {

        System.out.println("HandlerInterceptor1...preHandle");

        //return false表示拦截,不向下执行
        //return true表示放行
        return true;
    }

    //进入Handler方法之后,返回modelAndView之前执行
    //应用场景从modelAndView出发:将公用的模型数据(比如菜单导航)在这里传到视图,也可以在这里统一指定视图
    @Override
    public void postHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {

        System.out.println("HandlerInterceptor1...postHandle");

    }

    //执行Handler完成执行此方法
    //应用场景:统一异常处理,统一日志处理
    @Override
    public void afterCompletion(HttpServletRequest request,
            HttpServletResponse response, Object handler, Exception ex)
            throws Exception {

        System.out.println("HandlerInterceptor1...afterCompletion");
    }

}

8.2 拦截器配置

  • 8.2.1 针对HandlerMapping配置
    springmvc拦截器针对HandlerMapping进行拦截设置,如果在某个HandlerMapping中配置拦截,经过该 HandlerMapping映射成功的handler最终使用该 拦截器。

    <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"/>
    

    一般不推荐使用。

  • 8.2.2 类似全局的拦截器
    springmvc配置类似全局的拦截器,springmvc框架将配置的类似全局的拦截器注入到每个HandlerMapping中。

    <mvc:interceptors>
        <!--多个拦截器,顺序执行 -->
        <mvc:interceptor>
            <!-- /**表示所有url包括子url路径 -->
            <mvc:mapping path="/**"/>
            <bean class="cn.wyd.ssm.interceptor.HandlerInterceptor1"></bean>
        </mvc:interceptor>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="cn.wyd.ssm.interceptor.HandlerInterceptor2"></bean>
        </mvc:interceptor>
    </mvc:interceptors>
    

8.3 拦截测试

  • 8.3.1 测试需求
    测试多个拦截器各各方法执行时机。
  • 8.3.3 两个拦截器都放行

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

    总结:
    preHandle方法按顺序执行,postHandle和afterCompletion按拦截器配置的逆向顺序执行。

  • 8.3.4 拦截器1放行,拦截器2不放行

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

    总结:
    拦截器1放行,拦截器2 preHandle才会执行。拦截器2 preHandle不放行,拦截器2 postHandle和afterCompletion不会执行。只要有一个拦截器不放行,postHandle不会执行。

  • 8.3.5 拦截器1不放行,拦截器2不放行

    HandlerInterceptor1...preHandle

    拦截器1 preHandle不放行,postHandle和afterCompletion不会执行。拦截器1 preHandle不放行,拦截器2不执行。

8.4 小结

根据测试结果,对拦截器应用。比如:统一日志处理拦截器,需要该 拦截器preHandle一定要放行,且将它放在拦截器链接中第一个位置。比如:登陆认证拦截器,放在拦截器链接中第一个位置。权限校验拦截器,放在登陆认证拦截器之后。(因为登陆通过后才校验权限)

8.5 登陆认证拦截实现

/**
 * 
 * <p>Title: LoginInterceptor.java</p>
 * <p>Description: 登录认证拦截器</p>
 * <p>Company: www.wyd.com</p>
 * @author Wyd
 * @date 2017年11月29日
 * @version 1.0
 */
public class LoginInterceptor implements HandlerInterceptor {


    //进入 Handler方法之前执行
    //用于身份认证、身份授权
    //比如身份认证,如果认证通过表示当前用户没有登陆,需要此方法拦截不再向下执行
    @Override
    public boolean preHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler) throws Exception {

        //获取请求的url
        String url = request.getRequestURI();
        //判断url是否是公开 地址(实际使用时将公开 地址配置配置文件中)
        //这里公开地址是登陆提交的地址
        if(url.indexOf("login.action")>=0){
            //如果进行登陆提交,放行
            return true;
        }

        //判断session
        HttpSession session  = request.getSession();
        //从session中取出用户身份信息
        String username = (String) session.getAttribute("username");

        if(username != null){
            //身份存在,放行
            return true;
        }

        //执行这里表示用户身份需要认证,跳转登陆页面
        request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);

        //return false表示拦截,不向下执行
        //return true表示放行
        return false;
    }

    //进入Handler方法之后,返回modelAndView之前执行
    //应用场景从modelAndView出发:将公用的模型数据(比如菜单导航)在这里传到视图,也可以在这里统一指定视图
    @Override
    public void postHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {

        System.out.println("HandlerInterceptor1...postHandle");

    }

    //执行Handler完成执行此方法
    //应用场景:统一异常处理,统一日志处理
    @Override
    public void afterCompletion(HttpServletRequest request,
            HttpServletResponse response, Object handler, Exception ex)
            throws Exception {

        System.out.println("HandlerInterceptor1...afterCompletion");
    }

}
  • 拦截器配置
<!-- 登陆认证拦截器 -->
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <bean class="cn.wyd.ssm.interceptor.LoginInterceptor"></bean>
    </mvc:interceptor>

猜你喜欢

转载自blog.csdn.net/hnuwyd/article/details/79474556