Spring学习笔记之Spring MVC的高级技术
来源:互联网 发布:linux强制关闭程序 编辑:程序博客网 时间:2024/05/01 21:34
本篇主要介绍利用Spring MVC处理文件上传,异常处理,为控制器添加通知以及跨重定向请求传递数据。
1.Spring MVC处理文件上传
1.1使用multipart格式提交表单
在有文件上传的表单中,我们需要使用multipart格式的数据来上传,multipart格式的数据会将一个表单拆分成多个部分(part),每个部分对应一个输入域。在一半的表单输入域中,它所对应的部分会放置文本型数据,但是如果上传文件的话,它所对应的部分可以是二进制。下面是前段页面表单部分:
<form action="${ctx}/register" enctype="multipart/form-data" method="post"> <label>用户名:</label> <input type="text" name="username"><br/> <label>密码:</label> <input type="password" name="password"><br/> <label>邮箱:</label> <input type="text" name="email"><br/> <label>头像:</label> <input type="file" name="image" accept="image/jpeg,image/png,image/gif"><br/> <input type="submit" value="注册"> </form>
form表单现在将enctype属性设置为multipart/form-data,这会告诉浏览器以multipart数据的形式提交表单,而不是以表单数据的形式进行提交。在multipart中,每个输入域都会对应一个part。而文件上传输入域中的accept属性用来将文件类型限制为JPEG、PNG和GIF图片。
1.2配置multipart解析器
DispatcherServlet将解析multipart请求数据的任务委托给了Spring中MultipartResolver策略接口的实现。Spring内置了两个MultipartResolver的实现供我们选择:
- CommonsMultipartResolver:使用Jakarta Commons FileUpload解析multipart请求
- StandardServletMultipartResolver:依赖于Servlet3.0对multipart请求的支持。
使用Servlet 3.0解析multipart请求:
在Spring MVC上下文中将其声明为bean:
@Configuration @EnableWebMvc //启用Spring MVC@ComponentScan("spittr.web") //启用组件扫描public class WebConfig extends WebMvcConfigurerAdapter{ /** * 配置multipart解析器 * @return */ @Bean public MultipartResolver multipartResolver(){ return new StandardServletMultipartResolver(); }}
StandardServletMultipartResolver的配置是在Servlet中通过传入一个MultipartConfigElement实例来指定:
如果是自定义的Servlet,即(自己实现WebApplicationInitializer),这样做:
public class myServletIntializer implements WebApplicationInitializer{ public void onStartup(ServletContext servletContext) throws ServletException { MyServlet myServlet = new MyServlet(); Dynamic dynamic = servletContext.addServlet("myServlet", myServlet); dynamic.addMapping("/"); dynamic.setMultipartConfig( new MultipartConfigElement("/tmp/spittr/uploads")); }}
如果是通过继承AbstractAnnotationConfigDispatcherServletInitializer的到的DispatcherServlet的话,将不会有对Dynamic的引用,还记得上一篇我们提到的么,通过重载customizeRegistration会提供一个Dynamic参数,so:
@Override protected void customizeRegistration(Dynamic registration) { registration.setMultipartConfig( new MultipartConfigElement("/tmp/spittr/uploads")); }
这里,我们都是通过构造器来对MultipartConfigElement进行配置的。new MultipartConfigElement(location, maxFileSize, maxRequestSize, fileSizeThreshold)
这是其所有的构造器。
- location:用来指定上传文件的临时写入目录,一定要注意是绝对目录,比如Windows下的F:\Workspace\spring,上面我们配置的是Linux下的/tmp/spittr/uploads
- maxFileSize:上传文件的最大容量(以字节为单位),默认无限制
- maxRequestSize:整个multipart请求的最大容量(以字节为单位),不会关心有多少个part以及每个part的大小。默认是没有限制的
- fileSizeThreshold:在文件上传的过程中,如果文件大小达到了一个指定最大容量(以字节为单位),将会写入到临时文件路径中。默认值为0,也就是所有上床的文件都会写入到磁盘上。
下面是用web.xml来配置MultipartConfigElement,使用<servlet>
的<multipart-config>
元素:
<servlet> <servlet-name>appServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> <multipart-config> <location>/tmp/spittr/uploads</location> <max-file-size>2097152</max-file-size> <max-request-size>4194304</max-request-size> <file-size-threshold>0</file-size-threshold> </multipart-config> </servlet>
限制文件的大小为2MB,整个请求不超过4MB,而且所有的文件都写到磁盘里。
使用使用Jakarta Commons FileUpload解析multipart请求
声明为bean:
@Bean public MultipartResolver multipartResolver(){ //return new StandardServletMultipartResolver(); return new CommonsMultipartResolver(); }
CommonsMultipartResolver的配置不是在Servlet指定的,而是直接配置在实例中,而且CommonsMultipartResolver不会轻质要求设置临时文件路径,默认情况下,这个路径就是Servlet容器的临时目录。下面是配置一个等价于上面我们对MultipartConfigElement的配置:
@Bean public MultipartResolver multipartResolver() throws IOException{ //return new StandardServletMultipartResolver(); CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(); multipartResolver.setUploadTempDir( new FileSystemResource("/tmp/spittr/uploads"));//对应于MultipartConfigElement的location multipartResolver.setMaxUploadSize(2097152);//对应于MultipartConfigElement的maxFileSize multipartResolver.setMaxInMemorySize(0);//对应于MultipartConfigElement的fileSizeThreshold return multipartResolver; }
CommonsMultipartResolver 无法设定multipart请求整体的最大容量。
1.3处理multipart请求
1.3.1用byte[]数组来接受上传文件的二进制数据
代码:
@RequestMapping("/register") public void spittle( @RequestPart("image") byte[] image, User user){ System.out.println(image.length); }
1.3.2接受MultipartFile:
@RequestMapping("/register") public void spittle( @RequestPart("image") MultipartFile image, User user,HttpServletRequest request) throws IllegalStateException, IOException{ System.out.println(image.getContentType()); System.out.println(image.getName()); System.out.println(image.getOriginalFilename()); System.out.println(image.getSize()); image.transferTo( new File("/uploads/"+image.getOriginalFilename()));//将上传文件写入到文件系统中 }
需要注意的是这里File的位置是以你配置的临时文件路径为前提的,比如说你的临时文件路径为F:\Workspace\spring,那么上面的文件就是在F:\Workspace\spring\uploads\下
2.处理异常
Servlet请求的输出都是一个Servlet响应。如果在请求处理的时候,出现了异常,那它放入输出依然会使Servlet响应。异常必须要以某种方法转换为响应。
Spring提供了多种方式将异常转换为响应:
- 特定的Spring异常将会自动映射为指定的HTTP状态码
- 异常上可以添加@ResponseStatus注解,从而将其映射为某一个HTTP状态码
- 在方法上可以添加@ExceptionHandler注解,使其用来处理异常。
2.1将异常映射为HTTP状态码
Spring的一些异常会默认映射为HTTP状态码
下面介绍将异常映射为特定的状态码
首先我们创建一个异常,在其上面使用@ResponseStatus来将异常映射为特定的状态码。
@ResponseStatus(value=HttpStatus.NOT_FOUND, reason="Param Not Found")//将异常映射为HTTP状态404public class ParamNotFountException extends RuntimeException{ private static final long serialVersionUID = 1L;}
在控制器如果接受到的username为空则抛出这个异常:
@RequestMapping("/register") public String spittle( @RequestPart("image") MultipartFile image, User user,HttpServletRequest request){ //如果用户名为空则抛出异常 if(user.getUsername().isEmpty()){ throw new ParamNotFountException(); } return "register"; }
截图:
2.2编写异常处理的方法
继续上面的方法,如果我们不想显示错误页面,想要捕获异常的话,平时我们会使用catch进行捕获,但catch只能捕获当前语句块的异常并处理。下面我们来使用@ExceptionHandler标注的方法,它可以出来同一个控制器中所有处理器方法抛出的异常。接着上面的代码,我们在控制器方法下面写这样一个方法:
@RequestMapping("/register") public String spittle( @RequestPart("image") MultipartFile image, User user,HttpServletRequest request){ //如果用户名为空则抛出异常 if(user.getUsername().isEmpty()){ throw new ParamNotFountException(); } return "register"; } //捕获这个控制器抛出的ParamNotFountException异常,并处理 @ExceptionHandler(value=ParamNotFountException.class) public String HandleException(){ System.out.println("在这里处理异常"); return "error"; }
这一次,返回的不是404页面,而是error页面,而且控制台输出:“在这里处理异常”字段。
3.为控制器添加通知
控制器通知(controller advice)是任意带有@ControllerAdvice注解的类,这个类会包含一个或多个如下类型的方法:
- @ExceptionHandler注解标注的方法;
- @InitBinder注解标注的方法;
- @ModelAttribute注解标注的方法。
在带有@ControllerAdvice注解的类中,以上所述的这些方法会运用到整个应用程序所有控制器中带有@RequestMapping注解的方法上。@ControllerAdvice注解本身自带扫描特性,即使用了@Component。
还是上面的代码,我们把控制器中的HandleException()方法删掉,然后新建一个类,运行效果是一样的:
@ControllerAdvicepublic class AppWideException { //捕获所有控制器抛出的ParamNotFountException异常,并处理 @ExceptionHandler(value=ParamNotFountException.class) public String HandleException(){ System.out.println("在这里处理异常"); return "error"; }}
4.跨重定向请求传递数据
当一个处理器方法完成之后,该方法所指定的模型数据将会复制到请求中,并作为请求的属性,请求会转发(forward)到视图上进行渲染。因为控制器方法和视图所处理的是同一个请求,所以在转发的过程中,请求属性能够得以保存。当控制器的结果是重定向的话,原始的请求就结束了,并且会发起一个新的get请求。原始请求中所带有的模型数据也就随之请求一起消亡了。
有两种方法能够从发起重定向的方法传递数据给处理重定向方法中:
- 使用URL模板以路径变量和/或查询参数的形式传递数据
- 通过flash属性发生数据
4.1通过URL模板进行重定向
使用URL中的占位符进行传递:
@RequestMapping("/register") public String spittle(User user,Model model){ model.addAttribute("username", "xuexiaoqiang"); model.addAttribute("email", "test@tom.com"); //重定向 return "redirect:/test/{username}"; } @RequestMapping("/test/{username}") public String test( @PathVariable(value="username") String username, HttpServletRequest request){ String email = request.getParameter("email"); System.out.println("username:"+username); System.out.println("email:"+email); return "register"; }
输出:
username:xuexiaoqiangemail:test@tom.com
这里,因为模型中的email没有匹配重定向URL中的任何占位符,所以它会自动以查询参数的形式附加到重定向URL上。这里最后的重定向路径是“/test/xuexiaoqiang?email=Mr_xue@tom.com”。
4.2使用flash属性
如果我们想要发生一个对象,那么URL就不能实现,Spring提供了将数据发生为flash属性的功能。按照定义,flash属性会一直携带这些数据知道下一次请求,然后才会消失。
RedirectAttributes是Model的一个子接口,提供了Model的所有功能,除此之外,还有几个方法是用来设置flash属性的。
@RequestMapping("/register") public String spittle(User user,RedirectAttributes model){ user.setEmail("test@tom.com"); user.setUsername("xuexiaoqiang"); model.addFlashAttribute("user", user); //重定向 return "redirect:/test"; } @RequestMapping("/test") public String test(Model model){ if(model.containsAttribute("user")){ System.out.println("存在"); } return "testdata"; }
testdata.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>Insert title here</title></head><body><h1>用户名:${user.username }</h1><h1>邮箱:${user.email }</h1></body></html>
最后结果截图:
在重定向执行之前,所有flash属性都会复制到会话中。在重定向后,存在会话中的flash属性会被取出,并从会话转移到模型之中。处理重定向的方法就能从模型中访问User对象了,就像获取其他的模型对象一样。
- Spring学习笔记之Spring MVC的高级技术
- 一点一滴学习Spring(七)之Spring MVC的高级技术
- spring(7)spring mvc 的高级技术
- 第七章 Spring MVC 的高级技术
- Spring学习笔记之Spring MVC 入门教程
- Spring学习笔记之二: Spring MVC
- Spring学习笔记之Spring MVC
- Spring学习笔记之Spring MVC的工作机制
- Spring学习笔记-C7-SpringMVC高级技术
- Spring实战-读书笔记(七)-Spring MVC的高级技术
- 3.Spring学习笔记之高级装配
- 第7章 Spring MVC的高级技术--异常处理
- 第7章-Spring MVC的高级技术
- Spring MVC学习笔记(一)---Spring MVC 的HelloWorld
- spring mvc学习笔记之注解
- mybaties+Spring mvc学习之路笔记
- Spring学习笔记之Spring MVC体系学习(一)
- Spring MVC 学习笔记
- android NDK搭建与开发(一)
- Oracle数据库中文乱码问题解决
- c++ sort()与qsort()函数使用
- Android SurfaceView 多线程绘图
- 解决方法数越界
- Spring学习笔记之Spring MVC的高级技术
- 【莫队】莫队算法的扩展
- 兑换码编码方案实践
- 3.20
- 会话管理
- JQuery min.js .js 区别
- Linux中用stat命令查看文件时3个时间点解析
- 机器学习算法简单汇总
- css-响应Web设计2