1、配置其它的Servlet和Filter
按照 AbstractAnnotationConfigDispatcherServletInitializer 的定义,它会创
建 DispatcherServlet 和 ContextLoaderListener 。但是,如果你想注册其他的 Servlet 、 Filter 或 Listener 的话,那该怎么办呢?
基于 Java 的初始化器( initializer )的一个好处就在于我们可以定义任意数量的初始化器类。因此,如果我们想往 Web 容器中注册其他组件的
话,只需创建一个新的初始化器就可以了。最简单的方式就是实现 Spring 的 WebApplicationInitializer 接口。
不过,如果你只是注册 Filter ,并且该 Filter 只会映射到 DispatcherServlet 上的话,那么
在 AbstractAnnotationConfigDispatcherServletInitializer 中还有一种快捷方式。
为了注册 Filter 并将其映射到 DispatcherServlet ,所需要做的仅仅是重
载 AbstractAnnotationConfigDispatcherServletInitializer 的 getServlet-Filters() 方法。例如,在如下的代码中,重
载了 AbstractAnnotationConfig-DispatcherServletInitializer 的 getServletFilters() 方法以注册 Filter :
2、处理 multipart 形式的数据
简单的说就是form表单的文件框上传的处理,spring mvc需要配置处理器
从 Spring 3.1 开始, Spring 内置了两个 MultipartResolver 的实现供我们选择:
- CommonsMultipartResolver :使用 Jakarta Commons FileUpload 解析 multipart 请求;
- StandardServletMultipartResolver :依赖于 Servlet 3.0 对 multipart 请求的支持(始于 Spring 3.1 )
这里我们只说Servlet3.0形式的配置,也推荐这种方式,至少不会需要其它第三方的jar包:
第一步
兼容 Servlet 3.0 的 StandardServletMultipartResolver 没有构造器参数,也没有要设置的属性。这样,在 Spring 应用上下文中,将其
声明为 bean 就会非常简单,如下所示:
第二步
如果我们配置 DispatcherServlet 的 Servlet 初始化类继承了 Abstract
AnnotationConfigDispatcherServletInitializer 或 AbstractDispatcher-ServletInitializer 的话,那么我们不会直
接创建 DispatcherServlet 实例并将其注册到 Servlet 上下文中。这样的话,将不会有对 Dynamic Servlet registration 的引用供我们使用
了。但是,我们可以通过重载 customizeRegistration() 方法(它会得到一个 Dynamic 作为参数)来配置 multipart 的具体细节:
构造器支持设置参数:
- 上传文件的最大容量(以字节为单位)。默认是没有限制的。
- 整个 multipart 请求的最大容量(以字节为单位),不会关心有多少个 part 以及每个 part 的大小。默认是没有限制的。
- 在上传的过程中,如果文件大小达到了一个指定最大容量(以字节为单位),将会写入到临时文件路径中。默认值为 0 ,也就是所有上传
- 的文件都会写入到磁盘上
第三步
配置好了我们需要在控制器把文件接过来呀,可以使用MultipartFile,当然我们还可以使用Part,
通过 Part 参数的形式接受文件上传,那么就没有必要配置 MultipartResolver 了。只有使
用 MultipartFile 的时候,我们才需要 MultipartResolver 。不过需要注意临时路径必须配置,当然自己可以在接受过来之后自己不用提供的接口,自己写保存的逻辑。
@RequestMapping(value = "/upload", method = RequestMethod.POST) public ModelAndView upload(@RequestParam("file") MultipartFile file, HttpServletRequest request){ String msg = ""; try { File root = new File("D:/uploads/"); if(!root.exists()){ root.mkdir(); } File f = new File("/uploads/"+file.getOriginalFilename()); FileOutputStream fos = new FileOutputStream(f); fos.write(file.getBytes()); fos.flush(); fos.close(); msg = "上传成功!"; } catch (IOException e) { e.printStackTrace(); msg = "上传失败!"; } ModelAndView mav = new ModelAndView("result"); mav.addObject("msg",msg); return mav; } @RequestMapping(value = "/upload2" , method = RequestMethod.POST) public ModelAndView upload2(@RequestParam("file") Part file){ String msg = ""; try { System.out.println(file.getSubmittedFileName()); File f = new File("/uploads/"+file.getSubmittedFileName()); InputStream is = file.getInputStream(); FileOutputStream fos = new FileOutputStream(f); int index; byte[] bytes = new byte[1024]; while ((index = is.read(bytes)) != -1) { fos.write(bytes, 0, index); fos.flush(); } fos.close(); is.close(); msg = "上传成功!"; } catch (IOException e) { e.printStackTrace(); msg = "上传失败!"; } ModelAndView mav = new ModelAndView("result"); mav.addObject("msg",msg); return mav; }
3、处理异常
Spring 提供了多种方式将异常转换为响应:
- 特定的 Spring 异常将会自动映射为指定的 HTTP 状态码;
- 异常上可以添加 @ResponseStatus 注解,从而将其映射为某一个 HTTP 状态码;
- 在方法上可以添加 @ExceptionHandler 注解,使其用来处理异常。
特定异常转换为http状态码
异常上可以添加 @ResponseStatus 注解,从而将其映射为某一个 HTTP 状态码
import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ResponseStatus; @ResponseStatus(value = HttpStatus.NOT_FOUND , reason = "not found!") public class CodeFoundException extends RuntimeException { public CodeFoundException(){} public CodeFoundException(String msg){ super(msg); } }
在方法上可以添加 @ExceptionHandler 注解,使其用来处理异常
@RequestMapping(value = "/error" , method = RequestMethod.GET) public ModelAndView error(@RequestParam("code") String code){ if("1".equals(code)) //throw new CodeFoundException("就是测试一下!"); throw new DuplicateCodeFoundException("就是测试一下!"); ModelAndView mav = new ModelAndView("error"); return mav; } @ExceptionHandler(DuplicateCodeFoundException.class) public ModelAndView handleDuplicateCodeFound(){ ModelAndView mav = new ModelAndView("codefound"); return mav; }
如果控制器类的特定切面能够运用到整个应用程序的所有控制器中,那么这将会便利很多。举例来说,如果要在多个控制器中处理异常,
那 @ExceptionHandler 注解所标注的方法是很有用的。不过,如果多个控制器类中都会抛出某个特定的异常,那么你可能会发现要在所有
的控制器方法中重复相同的 @ExceptionHandler 方法。或者,为了避免重复,我们会创建一个基础的控制器类,所有控制器类要扩展这个
类,从而继承通用的 @ExceptionHandler 方法。
Spring 3.2 为这类问题引入了一个新的解决方案:控制器通知。控制器通知( controller advice )是任意带有 @ControllerAdvice 注解的类,
这个类会包含一个或多个如下类型的方法:
- @ExceptionHandler 注解标注的方法;
- @InitBinder 注解标注的方法;
- @ModelAttribute 注解标注的方法。
在带有 @ControllerAdvice 注解的类中,以上所述的这些方法会运用到整个应用程序所有控制器中带有 @RequestMapping 注解的方法
上。
@ControllerAdvice 注解本身已经使用了 @Component ,因此 @ControllerAdvice 注解所标注的类将会自动被组件扫描获取到,就像
带有 @Component 注解的类一样。
@ControllerAdvice 最为实用的一个场景就是将所有的 @ExceptionHandler 方法收集到一个类中,这样所有控制器的异常就能在一个地
方进行一致的处理。例如,我们想将 DuplicateSpittleException 的处理方法用到整个应用程序的所有控制器上。如下的程序清单展现
的 AppWideExceptionHandler 就能完成这一任务,这是一个带有 @ControllerAdvice 注解的类。
4、重定向和转发
在控制器方法返回的视图名称中,我们借助了 “redirect:” 前缀的力量。当控制器方法返回的 String 值以 “redirect:” 开头的
话,那么这个 String 不是用来查找视图的,而是用来指导浏览器进行重定向的路径
return new ModelAndView("redirect:/")
我们知道重定向是会丢失参数的,但是spring有一种保存数据方法使用 flash 属性。
转发,url不变,参数不丢失,前缀forward:
return new ModelAndView("forward:/dengluPost")