SpringMVC 之攔截器和異常處理


1. 文件上傳

  • Spring 使用 Jakarta Commons FileUpload 技術實現了一個 MultipartResolver 實現類:
    CommonsMultipartResolver;
  • SpringMVC 上下文中默認沒有裝配 MultipartResolver,因此默認情況下不能處理文件的上傳;
    若要使用上傳功能,需要在上下文中配置 MultipartResolver;
// 1. 導入 jar 包
      /* commons-fileupload;
       * commons-io;
       */

// 2. 配置 MultipartResolver(多媒體解析器)
<bean id="multipartResolver"
        class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="defaultEncoding" value="UTF-8"></property>

    <!-- 文件大小 1M -->
    <property name="maxUploadSize" value="1048576"/>
</bean>

// index.jsp
<h2>文件上傳</h2>
<form action="${pageContext.request.contextPath}/testUpload" method="post"
                                                    enctype="multipart/form-data">
    file1:<input type="file" name="upload"/><br/>
    file2:<input type="file" name="upload"/><br/>
    file3:<input type="file" name="upload"/><br/>
    <input type="submit" value="上傳"/><br/>
</form>


// Demo.java
@RequestMapping(value="/testUpload",method=RequestMethod.POST)
public String testUpload(@RequestParam("upload") MultipartFile[] file)
                                     throws IllegalStateException, IOException {

    for(MultipartFile mf : file){
        if(!mf.isEmpty()){
            mf.transferTo(new File("/Users/用戶名/Documents/上傳/"+
                                                          mf.getOriginalFilename()));
        }
    }
    return "ok";
}

2. 自定義攔截器

  1. 自定義的攔截器必須實現 HandlerInterceptor 接口:
    • preHandle():這個方法在業務處理器處理請求之前被調用,在該方法中對用戶請求 request 進行
      處理;如果攔截處理后,還需要調用其他的攔截器,或者是業務處理器,則返回 true; 否則,返回false;
    • postHandle():這個方法在業務處理器處理完請求后,但是,DispatcherServlet 向客戶端返回
      響應前被調用,處理用戶的 request請求;
    • afterCompletion():這個方法在 DispatcherServlet 完全處理完請求后被調用,可以在該方
      法中進行一些資源清理的操作;
// FirstInterceptor.java
public class FirstInterceptor implements HandlerInterceptor{
    public boolean preHandle(HttpServletRequest req,HttpServletResponse resp,
                                                Object handler)throws Exception{
        System.out.println("FirstInterceptor.....preHandle");
        return true;                                                    
    }

    public void postHandle(HttpServletRequest req, HttpServletResponse resp,
                    Object handler, ModelAndView modelAndView)throws Exception{
        System.out.println("FirstInterceptor.....postHandle");         
    }

    public void afterCompletion(HttpServletRequest req,HttpServletResponse resp,
                                  Object handler,Exception ex) throws Exception{
        System.out.println("FirstInterceptor.....afterCompletion");                    
    }
}

// 注冊該攔截器
<mvc:interceptors>
    <bean id="firstInterceptor"
                        class="cn.itcast.springmvc.interceptor.firstInterceptor"/>
</mvc:interceptors>

// index.jsp
示例:<a href="${pageContext.request.contextPath}/helloworld">點擊這里</a>

// Demo.java
@Controller
public class Demo{
    @RequestMapping(value="/hellowrold",method=RequestMethod.GET)
    public String helloworld(){
        System.out.println("======helloworld");
        return "ok";
    }
}
運行結果:

2.1 攔截器配置

// 自定義兩個攔截器
// FirstInterceptor.java(同上)
// SecondInterceptor.java
public class SecondInterceptor implements HandlerInterceptor{
    public boolean preHandle(HttpServletRequest req,HttpServletResponse resp,
                                                Object handler)throws Exception{
        System.out.println("SecondInterceptor.....preHandle");
        return true;                                                    
    }

    public void postHandle(HttpServletRequest req, HttpServletResponse resp,
                    Object handler, ModelAndView modelAndView)throws Exception{
        System.out.println("SecondInterceptor.....postHandle");         
    }

    public void afterCompletion(HttpServletRequest req,HttpServletResponse resp,
                                  Object handler,Exception ex) throws Exception{
        System.out.println("SecondInterceptor.....afterCompletion");                    
    }
}

// 注冊攔截器
<mvc:interceptors>
    <bean id="firstInterceptor"
                        class="cn.itcast.springmvc.interceptor.FirstInterceptor"/>

    <mvc:interceptor>
        <mvc:mapping path="/emps"></mvc:mapping>
        <bean id="secondInterceptor"
                        class="cn.itcast.springmvc.interceptor.SecondInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

// Demo.java
@Controller
public class Demo{
    @RequestMapping(value="/emps",method=RequestMethod.GET)
    public String list(Map<String,Object> map){

        System.out.println("emps .... 執行");
        map.put("employees",employeeService.getAll());
        return "list";
    }
}

// index.jsp
查詢所有員工: <a href="${pageContext.request.contextPath}/emps">查詢所有</a>
結果分析
  • 第一個攔截器 preHandler 返回 true,第二個也為 true;

  • 第一個攔截器返回 false,第二個為true或false;

  • 第一個攔截器返回 true,第二個為 false;

3. 異常處理

  1. SpringMVC 通過 HandlerExceptionResolver接口處理程序的異常,包括Handler映射,數據綁定以
    及目標方法執行時,發生的異常;
  2. SpringMVC 提供的 HandlerExceptionResolver接口的實現類:
    • ExceptionHandlerExceptionResolver;
    • DefaultHandlerExceptionResolver;
    • ResponseStatusExdeptionResolver;
    • SimpleMappingExceptionResolver;

3.1 HandlerExceptionResolver

  • @ExceptionHandler只處理當前 handler 方法中拋出的異常
// index.jsp
異常示例:<a href="{pageContext.request.contextPath}/testHandler?age=5">異常處理1</a>

// Demo.java
@Controller
public class Demo{

    @RequestMapping(value="/testHandler",method=RequestMethod.GET)
    public String testHandler(@RequestParam("age") Integer age){
        int result = 10 / age;
        System.out.println(result);
        return "ok";
    }

    // 如果請求參數 age=0, 會報異常

    // error.jsp
    <body>
    抱歉,系統繁忙,請稍候在試....
    </body>

    // 第一種方式: 返回到錯誤頁面,不帶異常信息
    @ExceptionHandler(value={ArithmeticException.class})
    public String dealHandlerExceptionResolver(Exception e){

        System.out.println("111111"+e.getMessage());
        return "error";
    }

    // 第二種方式: 將異常信息返回到錯誤頁面, 需要使用 ModelAndView, 不能使用 Map
    @ExceptionHandler(value={ArithmeticException.class})
    public ModelAndView dealHandlerExceptionResolver(Excetption e){

        System.out.println("22222"+e.getMessage());
        ModelAndView mv = new ModelAndView();
        mv.addObject("myexception",e.getMessage());
        mv.setViewName("error");
        return mv;
    }

    // error.jsp
    <body>
    抱歉,系統繁忙,請稍候在試....<br/>
    異常信息:${requestScope.myexception}
    </body>

    // 異常優先級問題
    // ArithmeticExcetion 繼承了 RuntimeException
    // 如果一個類中既有ArithmeticException, 也有 RuntimeException
    // 如果出現 ArithmeticException, 會執行ArithmeticException
    @ExceptionHandler(value={RuntimeException.class})
    public ModelAndView dealHandlerExceptionResolver2(Excetption e){

        System.out.println("33333"+e.getMessage());
        ModelAndView mv = new ModelAndView();
        mv.addObject("myexception",e.getMessage());
        mv.setViewName("error");
        return mv;
    }
}

3.2 @ControllerAdvice

  • 將所有異常存放在 exception 包下,將業務方法和處理異常的方法分離;
  • @ExceptionHandler中找不到的話,就去@ControllerAdvice標記的類里面查找標記了
    @ExceptionHandler的方法;
// cn.itcast.springmvc.exception.CommonHelperException 類

@ControllerAdvice
public class CommonHelperException{
    @ExceptionHandler(value={ArithmeticException.class})
    public ModelAndView dealHandlerExceptionResolver(Excetption e){

        System.out.println("44444"+e.getMessage());
        ModelAndView mv = new ModelAndView();
        mv.addObject("myexception",e.getMessage());
        mv.setViewName("error");
        return mv;
    }
}

3.3 ReponseStatusExceptionResolver

// 模擬賬戶鎖定,自定義一個 MyUserLockException 繼承了 RuntimeException 的異常類

// index.jsp
賬戶鎖定異常:
<a href="${pageContext.request.contextPath}/testResponse?username=lisi">異常示例</a>


// cn.itcast.springmvc.exception.MyUserLockException 類
@ResponseStatus(value=HttpStatus.LOCKED,reason="賬戶被鎖定,請撥打10086")
public class MyUserLockException extends RuntimeException{
    private static final long serialVersionUID = 1L;
}

// Demo.java
@Controller
public class Demo{
    @RequestMapping(value="/testResponse",method=RequestMethod.GET)
    public String testResponse(@RequestParam("username") String username){

        // username=zhangsan, 拋出異常
        if("zhangsan".equalsIgnoreCase(username)){
            throw new MyUserLockException();
        }
        return "ok";
    }

    //第二種用法,@ResponseStatus 用在方法上
    // 如果 name=zhangsan, 報 MyUserLockException,
    // 如果 name 為其他,報 Not Found
    @RequestMapping(value="/testResponse",method=RequestMethod.GET)
    @ResponseStatus(value=HttpStatus.NOT_FOUND,reason="測試...")
    public String testResponse(@RequestParam("username") String username){

        // username=zhangsan, 拋出異常
        if("zhangsan".equalsIgnoreCase(username)){
            throw new MyUserLockException();
        }
        return "ok";
    }
}

3.4 SimpleMappingExceptionResolver

// 示例: 數組下標越界異常

// springDispatcherServlet-servlet.xml 配置
<bean id="simpleMappingExceptionResolver"
    class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">

    <!-- 可以自定義 requestScope.異常名稱 --
    <property name="exceptionAttribute" value="自定義異常名稱"/>

    <property name="exceptionMappings">
    <props>
        <prop key="java.lang.ArrayIndexOutOfBoundsException">error</prop>
    </props>
    </property>
</bean>

// index.jsp
異常示例:<a href="${pageContext.request.contextPath}/testSimple?id=3">數組下標越界</a>

// error.jsp
<body>
抱歉,系統繁忙,請稍候在試!<br/>
數組下標越界:${requestScope.exception}<br/>
</body>

// Demo.java
@Controller
public class Demo{
    @RequestMapping(value="/testSimple",method=RequestMethod.GET)
    public String testSimple(@RequestParam("id") Integer id){

        int[] arr = new int[10];
        System.out.println(arr[id]);
        return "ok";
    }
}


參考資料


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM