Spring MVC 为文件上传提供了直接的支持,这种支持是通过即插即用的 MultipartResolver 实现的。Spring 用Jakarta Commons FileUpload 技术实现了一个MultipartResolver 实现类:CommonsMultipartResovler,因此需要依赖commons-fileupload.jar。
Spring MVC 上下文中默认没有装配 MultipartResovler,因此默认情况下不能处理文件的上传工作,如果想使用 Spring的文件上传功能,需要在上下文中配置 MultipartResolver:
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="UTF-8"/>
<property name="maxUploadSize" value="1024000"/>
bean>
文件上传示例:
@Controller
public class FileUploadController {
/**
* 文件上传(支持多个文件),单个文件时也可以使用MultipartFile作为方法参数
* @param request
* @return
* @throws IOException
*/
@RequestMapping(value = {"/multiFileUpload"})
public String 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");
}
}
拦截器注册:
<mvc:interceptors>
<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"/>
<mvc:interceptor>
<mvc:mapping path="/hello"/>
<bean class="com.intercepor.MyInterceptor"/>
mvc:interceptor>
mvc:interceptors>
Spring MVC 通过 HandlerExceptionResolver 处理程序的异常,包括 Handler 映射、数据绑定以及目标方法执行时发生的异常。HandlerExceptionResolver 接口及实现类如下:
SpringMVC默认装配AnnotationMethodHandlerExceptionResolver、ResponseStatusExceptionResolver和DefaultHandlerExceptionResolver三种HandlerExceptionResolver,当开启
时与HandlerMapping相似,AnnotationMethodHandlerExceptionResolver会被ExceptionHandlerExceptionResolver所替代。
主要处理 Handler 中用 @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 = {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中返回给客户端。
在异常及异常父类中找到 @ResponseStatus 注解,然后使用这个注解的属性进行处理。
/**
* 自定义异常类,使用@ResponseStatus定义状态码和message信息
*/
@ResponseStatus(value = 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";
}
对一些特殊的异常进行处理,比如NoSuchRequestHandlingMethodException、HttpRequestMethodNotSupportedException、HttpMediaTypeNotSupportedException、HttpMediaTypeNotAcceptableException等,具体看DefaultHandlerExceptionResolver的doResolveException方法源码。
如果希望对所有异常进行统一处理,可以使用SimpleMappingExceptionResolver,它将异常类名映射为视图名,即发生异常时使用对应的视图报告异常。
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="java.lang.ArrayIndexOutOfBoundsException">errorprop>
<prop key="java.lang.ClassNotFoundException">errorprop>
props>
property>
bean>
Exception会被放入ModelAndView中,可以在页面获取异常信息。