spring|SpringMVC文件上传、拦截器与异常处理

文件上传 Spring MVC 为文件上传提供了直接的支持,这种支持是通过即插即用的 MultipartResolver 实现的。Spring 用Jakarta Commons FileUpload 技术实现了一个MultipartResolver 实现类:CommonsMultipartResovler,因此需要依赖commons-fileupload.jar。
Spring MVC 上下文中默认没有装配 MultipartResovler,因此默认情况下不能处理文件的上传工作,如果想使用 Spring的文件上传功能,需要在上下文中配置 MultipartResolver:


【spring|SpringMVC文件上传、拦截器与异常处理】文件上传示例:
@Controller public class FileUploadController { /** * 文件上传(支持多个文件),单个文件时也可以使用MultipartFile作为方法参数 * @param request * @return * @throws IOException */ @RequestMapping(value = https://www.it610.com/article/{"/multiFileUpload"}) publicString multiFileUpload(MultipartHttpServletRequest request) throws IOException { //获取所有的文件上传表单内容 Map filesMap = request.getFileMap(); Set entries = filesMap.entrySet(); //获取项目真实路径 String realPath = request.getSession().getServletContext().getRealPath(""); System.out.println(realPath); //获取缓冲区 ByteBuffer byteBuffer = ByteBuffer.allocate(1024); for (Map.Entry entry : entries) {String key = entry.getKey(); MultipartFile file = entry.getValue(); //获取原始文件名 String filename = file.getOriginalFilename(); //创建输入/输出通道 FileInputStream fileInputStream = null; FileChannel inChannel = null; FileOutputStream fileOutputStream = null; FileChannel outChannel = null; try { fileInputStream = (FileInputStream)file.getInputStream(); inChannel = fileInputStream.getChannel(); fileOutputStream = new FileOutputStream(new File(realPath+"\\WEB-INF\\classes\\"+filename)); outChannel = fileOutputStream.getChannel(); //上传文件 while (inChannel.read(byteBuffer) != -1) { byteBuffer.flip(); outChannel.write(byteBuffer); byteBuffer.clear(); } System.out.println("上传文件的name属性" + key + "原始文件名:" + filename); } catch (IOException e) { e.printStackTrace(); } finally { if (fileInputStream != null) { fileInputStream.close(); } if (fileOutputStream != null) { fileOutputStream.close(); } if (inChannel != null) { inChannel.close(); } if (outChannel != null) { outChannel.close(); } }} return "success"; } }

拦截器 在SpringMVC源码分析之数据绑定与国际化中我们提到了LocaleChangeInterceptor拦截器用于拦截locale参数实现国际化切换。SpringMVC提供了HandlerInterceptor接口可用于自定义拦截器。
自定义拦截器示例:
public class MyInterceptor implements HandlerInterceptor { /** * @desc 在处理器实际执行之前执行 * 当方法返回 true时,处理器链会继续执行; * 若方法返回 false, DispatcherServlet即认为拦截器自身已经完成了 * 对请求的处理(比如说,已经渲染了一个合适的视图),那么其余的拦 * 截器以及执行链中的其他处理器就不会再被执行了 */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("preHandle"); return true; } /** * @desc 在处理器实际执行之后执行 */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("postHandler"); } /** * @desc 出现异常或在整个请求处理完成之后被执行 */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("afterCompletion"); } }

拦截器注册:

异常处理 Spring MVC 通过 HandlerExceptionResolver 处理程序的异常,包括 Handler 映射、数据绑定以及目标方法执行时发生的异常。HandlerExceptionResolver 接口及实现类如下:
spring|SpringMVC文件上传、拦截器与异常处理
文章图片

SpringMVC默认装配AnnotationMethodHandlerExceptionResolver、ResponseStatusExceptionResolver和DefaultHandlerExceptionResolver三种HandlerExceptionResolver,当开启时与HandlerMapping相似,AnnotationMethodHandlerExceptionResolver会被ExceptionHandlerExceptionResolver所替代。
ExceptionHandlerExceptionResolver 主要处理 Handler 中用 @ExceptionHandler 注解定义的方法。
  • @ExceptionHandler 注解定义的方法总是以更具体的Exception优先:例如发生的是NullPointerException,但是声明的异常有NullPointerException和RuntimeException ,此时会以NullPointerException优先
  • ExceptionHandlerMethodResolver在处理器中若找不到@ExceptionHandler 注解的方法,会找@ControllerAdvice 中的@ExceptionHandler 注解方法
@RequestMapping("/testException") public String testException(@RequestParam("i") int i) { System.out.println(10/i); return "success"; }/** * 在@ExceptionHandler方法的入参中可以加入Exception类型的参数,该参数即对应发生的异常对象 * 如果想把异常信息传递给页面,不可以在方法参数中使用Map等参数,可以返回ModelAndView对象 * @param e * @return */ @ExceptionHandler(value = https://www.it610.com/article/{ArithmeticException.class}) public ModelAndView handleArithmeticException(Exception e) { ModelAndView mv = new ModelAndView("error"); mv.addObject("exception",e); System.out.println("出现异常:"+e); return mv; }

@ExceptionHandler方法也可以放在标注了@ControllerAdvice的类中作为全局处理。实际项目中,我们可以自定义异常做统一的封装然后通过@ControllerAdvice将异常信息存入ModelAndView中返回给客户端。
ResponseStatusExceptionResolver 在异常及异常父类中找到 @ResponseStatus 注解,然后使用这个注解的属性进行处理。
  • 定义一个 @ResponseStatus 注解修饰的异常类
  • 如果上述异常没有被ExceptionHandlerExceptionResolver解析,则会被ResponseStatusExceptionResolver 解析,根据@ResponseStatus 中注解的信息返回给客户端。
/** * 自定义异常类,使用@ResponseStatus定义状态码和message信息 */ @ResponseStatus(value = https://www.it610.com/article/HttpStatus.BAD_REQUEST,reason ="用户名与密码不匹配") public class UserNamePasswordNotMatchException extends Exception {}@RequestMapping("/testException") public String testException(@RequestParam("i") int i) throws UserNamePasswordNotMatchException { try { System.out.println(10/i); } catch (Exception e) { throw new UserNamePasswordNotMatchException(); } return "success"; }

DefaultHandlerExceptionResolver 对一些特殊的异常进行处理,比如NoSuchRequestHandlingMethodException、HttpRequestMethodNotSupportedException、HttpMediaTypeNotSupportedException、HttpMediaTypeNotAcceptableException等,具体看DefaultHandlerExceptionResolver的doResolveException方法源码。
SimpleMappingExceptionResolver 如果希望对所有异常进行统一处理,可以使用SimpleMappingExceptionResolver,它将异常类名映射为视图名,即发生异常时使用对应的视图报告异常。
error error

Exception会被放入ModelAndView中,可以在页面获取异常信息。

    推荐阅读