SSM-SpringMVC(三)-特殊请求参数、异常处理
特殊的请求参数
集合类型
-
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>
- 用户名输入123,age输入456,勾选1、3,则请求路径为:
/test1?name=123&age=456&hoppy=1&hoppy=3
- 用户名输入123,age输入456,勾选1、3,则请求路径为:
-
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!"; }
- 如果为map打印为:
{name=123,age=456,hoppy=1}
- 如果为map和
String[]
,打印为:{name=123,age=456,hoppy=1} 1 3
- 注意:数组不需要用
@RequestParam
修饰
- 注意:数组不需要用
- 如果为map和
List
,打印为:{name=123,age=456,hoppy=1} 1 3
- 如果为map和
Set
,打印为:{name=123,age=456,hoppy=1} 1 3
- 如果为map打印为:
-
即可以将请求参数自动封装成集合进行接收
multipart参数
-
添加依赖
<dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.4</version> </dependency>
-
添加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>
-
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>
-
Controller监听服务
@RequestMapping("/test2") @ResponseBody public String test2(String username, String password) { System.out.println(username); System.out.println(password); return "test2 success!"; }
文件上传
- 前提条件是在【multipart参数】配置的基础上
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>
-
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.多个文件上传
-
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>
-
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的常用属性
- defaultEncoding: 设置request的请求编码
- uploadTempDir: 设置上传文件时的临时目录,默认是Servlet容器的临时目录
- maxUploadSize::限制总的上传文件大小,以字节为单位。当设为-1时表示无限制,默认是-1
- maxUploadSizePerFile:限制每个上传文件的大小
- maxInMemorySize:设置每个文件上传时允许写到内存中的最大值,以字节为单位,默认是10240
- 若一个文件的大小超过这个数值,就会生成临时文件;否则不会生成临时文件
日期类型
- Spring(MVC)默认支持yyyy/MM/dd的日期格式转换,其他日期格式需要特殊处理
-
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!";
}
- 这种方法有个弊端,每个接收都要设置
方法二:使用Converter
-
创建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; } } }
- IoC容器配置
-
配置conversionService转换器
<!-- 类型转换器 --> <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <set> <bean class="com.zh.converter.DateConverter"/> </set> </property> </bean>
-
SpringMVC设置转换器
<mvc:annotation-driven conversion-service="conversionService"> ... </mvc:annotation-driven>
-
-
Controller接收
@RequestMapping("/test5") @ResponseBody public String test5(Date birthday) throws Exception { System.out.println(birthday); return "test5 success!"; }
异常处理
- 之前讲过的异常处理有2种方式:
- throws抛出
- try-catch处理
- 项目的调用层是:Controller->Service->dao层
- 可以将异常通过dao一层一层向上抛出
- 如果Controller也不处理,则抛给DispatcherServlet
- 如果DispatcherServlet也不处理,则抛给Tomcat
- 因此至少要在Controller进行异常处理,否则就会报出服务器异常错误
- 如果在Controller的每个方法都添加try-catch的话,会显得很麻烦臃肿
- 而且每次try-catch的内容基本都一样,遇到异常跳转到统一自定义的异常页面
- SpringMVC可以对异常信息作统一的处理,主要有4种方式
- 使用SpringMVC自带的异常处理类:SimpleMappingExceptionResolver
- 自定义异常处理类:实现HandlerExceptionResolver接口
- 使用注解:@ExpcetionHandler
- 使用注解:@ExpcetionHandler+@ControllerAdvice
使用SpringMVC自带SimpleMappingExceptionResolver
-
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"); }
-
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>
-
runtime.jsp
<body> This is runtime.jsp.异常信息:${ex.message} </body>
-
注意: 该方法处理异常不能传递自定义的数据到jsp
自定义异常处理类
-
自定义异常类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; } }
- 将该类添加到IoC容器中即可
-
方法一:在applicationContext.xml中
<bean class="com.zh.resolver.MyExceptionResolver"/>
-
方法二:或者在MyExceptionResolver类定义前添加注解:
@Component
//扫描所有注解到IoC容器中 <context:component-scan base-package="com.zh"/>
-
-
default.jsp
<body> This is default.jsp.异常信息:${ex.message}.其他:${name} </body>
@ExpcetionHandler
- @ExpcetionHandler方法、出现异常的方法,必须处在同一个Controller中
-
这也就意味着,这种方法只能处理一个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. 添加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; } }
- @ControllerAdvice可以通过属性设置它的处理范围
- basePackages、basePackageClasses、assignableTypes、annotations
-
添加到IoC容器
//扫描所有注解到IoC容器中,上面的@ControllerAdvice注解,也属于@Component,可以被扫描 <context:component-scan base-package="com.zh"/>