• 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";
        }
    }
    


    参考资料

  • 相关阅读:
    吴裕雄--天生自然 R语言开发学习:高级编程
    吴裕雄--天生自然 R语言开发学习:使用ggplot2进行高级绘图(续二)
    吴裕雄--天生自然 R语言开发学习:使用ggplot2进行高级绘图(续一)
    吴裕雄--天生自然 R语言开发学习:使用ggplot2进行高级绘图
    吴裕雄--天生自然 R语言开发学习:处理缺失数据的高级方法(续一)
    吴裕雄--天生自然 R语言开发学习:处理缺失数据的高级方法
    吴裕雄--天生自然 R语言开发学习:分类(续二)
    2018年阿里云NoSQL数据库大事盘点
    就差这2块钱的安全投入,让这家企业损失了1977万!
    如何打造7*24h持续交付通道?阿里高级技术专家的5点思考
  • 原文地址:https://www.cnblogs.com/linkworld/p/7783036.html
Copyright © 2020-2023  润新知