SSM-SpringMVC(三)-特殊请求参数、异常处理

特殊的请求参数

集合类型

  1. index.jsp中的代码

     <form action="test1">
         <input name="name">
         <input name="age">
         <div>
             足球<input type="checkbox" value="1" name="hobby">
         </div>
         <div>
             篮球<input type="checkbox" value="2" name="hobby">
         </div>
         <div>
             台球<input type="checkbox" value="3" name="hobby">
         </div>
         <button type="submit">提交</button>
     </form>
    
    1. 用户名输入123,age输入456,勾选1、3,则请求路径为:/test1?name=123&age=456&hoppy=1&hoppy=3
  2. Controller的处理函数参数类型为集合类型

     @RequestMapping("/test1")
     @ResponseBody
     //{name=123,age=456,hoppy=1} 1 3
     public String test1(
     @RequestParam Map<String, Object> map,
     // String[] hobby,
     // @RequestParam(required = true) List<String> hobby,
     @RequestParam(required = false) Set<String> hobby) {
         System.out.println(map);
         for (String s : hobby) {
             System.out.println(s);
         }
         return "success!";
     }
    
    1. 如果为map打印为:{name=123,age=456,hoppy=1}
    2. 如果为map和String[],打印为:{name=123,age=456,hoppy=1} 1 3
      1. 注意:数组不需要用@RequestParam修饰
    3. 如果为map和List,打印为:{name=123,age=456,hoppy=1} 1 3
    4. 如果为map和Set,打印为:{name=123,age=456,hoppy=1} 1 3
  3. 即可以将请求参数自动封装成集合进行接收

multipart参数

  1. 添加依赖

     <dependency>
         <groupId>commons-fileupload</groupId>
         <artifactId>commons-fileupload</artifactId>
         <version>1.4</version>
     </dependency>
    
  2. 添加CommonsMultipartResolver到IoC容器,id值固定为multipartResolver

     //zpplicationContext.xml
     <!-- 解析Multipart参数 -->
     <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
         <!--保证请求参数,文件名不乱吗-->
         <property name="defaultEncoding" value="UTF-8"/>
         <property name="maxInMemorySize" value="20480"/>
     </bean>
    
  3. index.jsp:传递multipart类型的参数

     <div>
         <form action="test2" method="post" enctype="multipart/form-data">
             <input name="username">
             <input name="password">
             <button type="submit">提交</button>
         </form>
     </div>
    
  4. Controller监听服务

     @RequestMapping("/test2")
     @ResponseBody
     public String test2(String username, String password) {
         System.out.println(username);
         System.out.println(password);
    
         return "test2 success!";
     }
    

文件上传

  1. 前提条件是在【multipart参数】配置的基础上

1.单个文件上传

  1. index.jsp

     <div>
         <form action="test3" method="post" enctype="multipart/form-data">
             <input name="username">
             <input type="file" name="photo1">
             <input type="file" name="photo2">
             <button type="submit">提交</button>
         </form>
     </div>
    
  2. Controller

     @RequestMapping("/test3")
     @ResponseBody
     public String test3(String username,
                         MultipartFile photo1,
                         MultipartFile photo2,
                         HttpServletRequest request) throws Exception {
         System.out.println(username);
    
         // 将文件数据写入到具体的位置
         //获取文件名称
         String filename = photo1.getOriginalFilename();
         //要写入的文件路径
         String path = request.getServletContext().getRealPath("upload/img/" + filename);
         //创建文件对象
         File file = new File(path);
         //将文件数据写入
         photo1.transferTo(file);
    
         filename = photo2.getOriginalFilename();
         path = request.getServletContext().getRealPath("upload/img/" + filename);
         file = new File(path);
         photo2.transferTo(file);
    
         return "test3 success!";
     }
    

2.多个文件上传

  1. index.jsp

     <div>
         <form action="test4" method="post" enctype="multipart/form-data">
             <input name="username">
             <input type="file" name="photos">
             <input type="file" name="photos">
             <button type="submit">提交</button>
         </form>
     </div>
    
  2. Controller

     @RequestMapping("/test4")
     @ResponseBody
     //List接收
     //public String test4(String username,List<MultipartFile> photos) throws Exception {
     //数组接收
     public String test4(String username,
                         MultipartFile[] photos) throws Exception {
         System.out.println(username);
         for (MultipartFile photo : photos) {
             System.out.println(photo.getOriginalFilename());
         }
         return "test4 success!";
     }
    

CommonsMultipartResolver的常用属性

  1. defaultEncoding: 设置request的请求编码
  2. uploadTempDir: 设置上传文件时的临时目录,默认是Servlet容器的临时目录
  3. maxUploadSize::限制总的上传文件大小,以字节为单位。当设为-1时表示无限制,默认是-1
  4. maxUploadSizePerFile:限制每个上传文件的大小
  5. maxInMemorySize:设置每个文件上传时允许写到内存中的最大值,以字节为单位,默认是10240
    1. 若一个文件的大小超过这个数值,就会生成临时文件;否则不会生成临时文件

日期类型

  1. Spring(MVC)默认支持yyyy/MM/dd的日期格式转换,其他日期格式需要特殊处理
  2. index.jsp

     <div>
         日期处理:
         <form action="test5" method="post">
             <input name="birthday" type="date">
             <button type="submit">提交</button>
         </form>
     </div>
    

方法一:使用@DateTimeFormat

@RequestMapping("/test5")
@ResponseBody
public String test5(@DateTimeFormat(pattern = "yyyy-MM-dd") Date birthday)
        throws Exception {
    System.out.println(birthday);
    return "test5 success!";
}
  1. 这种方法有个弊端,每个接收都要设置

方法二:使用Converter

  1. 创建DateConverter类

     //com.zh.converter.DateConverter
     public class DateConverter implements Converter<String, Date> {
         @Override
         public Date convert(String source) {
             try {
                 return new SimpleDateFormat("yyyy-MM-dd").parse(source);
             } catch (ParseException e) {
                 e.printStackTrace();
                 return null;
             }
         }
     }
    
  2. IoC容器配置
    1. 配置conversionService转换器

       <!-- 类型转换器 -->
       <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
           <property name="converters">
               <set>
                   <bean class="com.zh.converter.DateConverter"/>
               </set>
           </property>
       </bean>
      
    2. SpringMVC设置转换器

       <mvc:annotation-driven conversion-service="conversionService">
       ...
       </mvc:annotation-driven>
      
  3. Controller接收

     @RequestMapping("/test5")
     @ResponseBody
     public String test5(Date birthday)
             throws Exception {
         System.out.println(birthday);
         return "test5 success!";
     }
    

异常处理

  1. 之前讲过的异常处理有2种方式:
    1. throws抛出
    2. try-catch处理
  2. 项目的调用层是:Controller->Service->dao层
    1. 可以将异常通过dao一层一层向上抛出
    2. 如果Controller也不处理,则抛给DispatcherServlet
    3. 如果DispatcherServlet也不处理,则抛给Tomcat
    4. 因此至少要在Controller进行异常处理,否则就会报出服务器异常错误
    5. 如果在Controller的每个方法都添加try-catch的话,会显得很麻烦臃肿
    6. 而且每次try-catch的内容基本都一样,遇到异常跳转到统一自定义的异常页面
  3. SpringMVC可以对异常信息作统一的处理,主要有4种方式
    1. 使用SpringMVC自带的异常处理类:SimpleMappingExceptionResolver
    2. 自定义异常处理类:实现HandlerExceptionResolver接口
    3. 使用注解:@ExpcetionHandler
    4. 使用注解:@ExpcetionHandler+@ControllerAdvice

使用SpringMVC自带SimpleMappingExceptionResolver

  1. Controller中模拟异常

     @RequestMapping("/test1")
     public void test1() {
         throw new ArithmeticException("test1");
     }
    
     @RequestMapping("/test2")
     public void test2() throws Exception {
         throw new ClassNotFoundException("test2");
     }
    
     @RequestMapping("/test3")
     public void test3() throws Exception {
         throw new IOException("test3");
     }
    
     @RequestMapping("/test4")
     public void test4() {
         throw new ClassCastException("test4");
     }
    
  2. IoC容器中统一配置异常接收

     <!-- 异常处理 -->
     <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
         <property name="exceptionMappings">
             <props>
                 <!--key值为异常类型 -->
                 <prop key="java.lang.ArithmeticException">/WEB-INF/page/error/runtime.jsp</prop>
                 <prop key="java.io.IOException">/WEB-INF/page/error/io.jsp</prop>
                 <prop key="java.lang.ClassNotFoundException">/WEB-INF/page/error/not.jsp</prop>
             </props>
         </property>
         <!--将异常信息传递给jsp-->
         <property name="exceptionAttribute" value="ex"/>
         <!--默认的错误页面-->
         <property name="defaultErrorView" value="/WEB-INF/page/error/default.jsp"/>
     </bean>
    
  3. runtime.jsp

     <body>
     This is runtime.jsp.异常信息:${ex.message}
     </body>
    
  4. 注意: 该方法处理异常不能传递自定义的数据到jsp

自定义异常处理类

  1. 自定义异常类MyExceptionResolver并且实现HandlerExceptionResolver接口,统一处理项目中所有方法抛出的异常

     //@Component
     public class MyExceptionResolver implements HandlerExceptionResolver {
         @Override
         public ModelAndView resolveException(HttpServletRequest request,
                                              HttpServletResponse response,
                                              Object handler,
                                              Exception ex) {
        
        
             // HandlerMethod method = (HandlerMethod) handler;
             //异常是那个对象抛出的
             //method.getBean();
             //获取到异常是哪个方法抛出的
             //method.getMethod();
        
             ModelAndView mv = new ModelAndView();
             //传递信息
             mv.addObject("ex", ex);
             mv.addObject("name", "ZH");
             mv.setViewName("/WEB-INF/page/error/default.jsp");
             return mv;
         }
     }
    
  2. 将该类添加到IoC容器中即可
    1. 方法一:在applicationContext.xml中

       <bean class="com.zh.resolver.MyExceptionResolver"/>
      
    2. 方法二:或者在MyExceptionResolver类定义前添加注解:@Component

       //扫描所有注解到IoC容器中
       <context:component-scan base-package="com.zh"/>
      
  3. default.jsp

     <body>
     This is default.jsp.异常信息:${ex.message}.其他:${name}
     </body>
    

@ExpcetionHandler

  1. @ExpcetionHandler方法、出现异常的方法,必须处在同一个Controller中
  2. 这也就意味着,这种方法只能处理一个Controller方法中的异常

     @Controller
     public class ExceptionController {
         @RequestMapping("/test1")
         public void test1() {
             throw new ArithmeticException("test1");
         }
        
         @RequestMapping("/test2")
         public void test2() throws Exception {
             throw new ClassNotFoundException("test2");
         }
        
         @RequestMapping("/test3")
         public void test3() throws Exception {
             throw new IOException("test3");
         }
        
         @RequestMapping("/test4")
         public void test4() {
             throw new ClassCastException("test4");
         }
        
         //处理test1、test3异常
         @ExceptionHandler({ArithmeticException.class, IOException.class})
         public ModelAndView resolveException1(Exception ex) {
             ModelAndView mv = new ModelAndView();
             mv.addObject("ex", ex);
             mv.setViewName("/WEB-INF/page/error/runtime.jsp");
             return mv;
         }
        
         //处理test2、test4异常
         @ExceptionHandler
         public ModelAndView resolveException2(Exception ex) {
             ModelAndView mv = new ModelAndView();
             mv.addObject("ex", ex);
             mv.setViewName("/WEB-INF/page/error/default.jsp");
             return mv;
         }
     }
    

@ControllerAdvice+@ExceptionHandler

  1. 任意自定义一个类处理异常信息即可

     //1. 添加ControllerAdvice注解标识
     //拦截项目中所有方法的异常
     @ControllerAdvice
     //只拦截这个包下面的所有异常
     //@ControllerAdvice(basePackages = "com.zh.controller")
     //拦截这个类ExceptionController所在包的所有类的异常处理
     //@ControllerAdvice(basePackageClasses = ExceptionController.class)
     //只拦截这个类ExceptionController抛出的异常
     //@ControllerAdvice(assignableTypes = ExceptionController.class)
     //拦截带有@Controller注解的类抛出的异常
     //@ControllerAdvice(annotations = Controller.class)
     public class MyExceptionResolver {
         //2. 添加ExceptionHandler注解标识
         @ExceptionHandler({ArithmeticException.class, IOException.class})
         public ModelAndView resolveException1(Exception ex) {
             ModelAndView mv = new ModelAndView();
             mv.addObject("ex", ex);
             mv.setViewName("/WEB-INF/page/error/runtime.jsp");
             return mv;
         }
        
         @ExceptionHandler
         public ModelAndView resolveException2(Exception ex) {
             ModelAndView mv = new ModelAndView();
             mv.addObject("ex", ex);
             mv.setViewName("/WEB-INF/page/error/default.jsp");
             return mv;
         }
     }
    
    1. @ControllerAdvice可以通过属性设置它的处理范围
    2. basePackages、basePackageClasses、assignableTypes、annotations
  2. 添加到IoC容器

     //扫描所有注解到IoC容器中,上面的@ControllerAdvice注解,也属于@Component,可以被扫描
     <context:component-scan base-package="com.zh"/>
    
Table of Contents