内容概要
1.文件的上传
2.使用拦截器
3.异常处理
文件上传
Spring MVC 为文件上传提供了直接的支持,这种支持是通过即插即用的 MultipartResolver 实现的。Spring 用Jakarta Commons FileUpload 技术实现了一个 MultipartResolver 实现类:CommonsMultipartResovler
Spring MVC 上下文中默认没有装配 MultipartResovler,因此默认情况下不能处理文件的上传工作,如果想使用 Spring的文件上传功能,需要在上下文中配置 MultipartResolver
配置 MultipartResolver
defaultEncoding: 必须和用户 JSP 的 pageEncoding 属性一致,以便正确解析表单的内容
为了让 CommonsMultipartResovler 正确工作,必须先 将 Jakarta Commons FileUpload 及 Jakarta Commons io的类包添加到类路径下。
<!--文件上传皆是器-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--字符编码-->
<property name="defaultEncoding" value="utf-8"/>
<!--上传文件最大内存缓冲区1M,一次读1M然后写入文件-->
<property name="maxInMemorySize" value="1024000"/>
<!--上传文件的最大值100M-->
<property name="maxUploadSize" value="102400000"/>
</bean>
上传文件所需要的依赖
<!--文件上传-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
上传文件的示例
前端界面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="uploadFile" method="post" enctype="multipart/form-data">
文件:<input type="file" name="file">
文件:<input type="file" name="file">
文件:<input type="file" name="file">
文件:<input type="file" name="file">
<input type="submit" value="提交">
</form>
</body>
</html>
后端界面
@Controller
public class UploadController {
@RequestMapping("/uploadFile")//前端参数名一样时,自动封装成一个数组。
public String uploadFile(MultipartFile[] file, HttpServletRequest request) throws IOException {
//获取服务器存放上传文件的绝对路径
String realPath = request.getServletContext().getRealPath("/upload/");
File file1 = new File(realPath);
if (!file1.exists()){//判断目录是否存在,不存在就创建
file1.mkdirs();
}
System.out.println(file1);
Arrays.stream(file).forEach(ff->{
//获取上传文件的名称
String name = ff.getOriginalFilename();
//获取文件的大小
long size = ff.getSize();
//获取上传文件的类型
String fileType = ff.getContentType();
//上传文件
try {//内部封装了IO流
ff.transferTo(new File(file1,name));
} catch (IOException e) {
e.printStackTrace();
}
});
return "success";
}
}
文件的下载示例(不推荐使用)
前端代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<a href="download?fileName=aa.png">aa.png</a>
</body>
</html>
后端代码
@Controller
public class DownLoadController {
@RequestMapping("/download")
public ResponseEntity<byte[]> download(HttpServletRequest request,
HttpServletResponse response,
String fileName) throws Exception {
//服务器上下载文件存放的绝对路径地址
String realPath = request.getServletContext().getRealPath("/upload/");
//下载的文件对象
File file = new File(realPath,fileName);
FileInputStream fis = new FileInputStream(file);
//创建文件大小的字节
byte[] bytes = new byte[fis.available()];
//把文件内容读取到字节数组中
fis.read(bytes);
//设置http协议头
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add("Content-Disposition", "attchement;filename=" + URLEncoder.encode(file.getName(), "utf-8"));
return new ResponseEntity<>(bytes, httpHeaders, HttpStatus.OK);
}
}
该方法比较占用内存,推荐使用servlet的文件下载方法
自定义拦截器
Spring MVC也可以使用拦截器对请求进行拦截处理,用户可以自定义拦截器来实现特定的功能,自定义的拦截器必须实现HandlerInterceptor接口
– preHandle():这个方法在业务处理器处理请求之前被调用,在该方法中对用户请求 request 进行处理。如果程序员决定该拦截器对请求进行拦截处理后还要调用其他的拦截器,或者是业务处理器去进行处理,则返回true;如果程序员决定不需要再调用其他的组件去处理请求,则返回false。
– postHandle():这个方法在业务处理器处理完请求后,但是DispatcherServlet 向客户端返回响应前被调用,在该方法中对用户请求request进行处理。
– afterCompletion():这个方法在 DispatcherServlet 完全处理完请求后被调用,可以在该方法中进行一些资源清理的操作。
自定义拦截器示例
自定义拦截器类
/**
* 自定义拦截器,实现HandlerInterceptor接口,重写三个方法
*/
public class TestInterceptor implements HandlerInterceptor {
/**
* 请求拦截之前要做的事情,返回false,阻止请求到Controller处理类的方法,只有返回true才放行请求
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("1拦截之前");
return true;
}
/**
* 请求已经到达controller类的方法,执行完方法,在返回视图之前
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("1拦截中");
}
/**
* 请求响应结束后,controller方法执行返回后,做收尾工作,比如日志记录等
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("1拦截之后");
}
}
在Spring配置文件中配置
<!--配置自定义拦截器,拦截器业务处理类和拦截的请求地址,/**为根路径下的所有请求,不管目录层次。/*为一层路径下的所有请求-->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="interceptor.TestInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
当配置多个拦截器时的执行顺序按照配置文件从上往下执行:
1拦截之前
2拦截之前
3拦截之前
3拦截之后
2拦截之后
1拦截之后
1拦截之前
2拦截之前
3拦截之前
3拦截之后
2拦截之后
1拦截之后
拦截器与过滤器的区别
过滤器可以过滤所有请求,拦截器不能拦截静态资源的请求地址
异常处理
注意:异常处理类要被Spring容器扫描到
Spring MVC 通过 HandlerExceptionResolver 处理程序的异常,包括 Handler 映射、数据绑定以及目标方法执行时发生的异常。
SpringMVC 提供的 HandlerExceptionResolver 的实现类
HandlerExceptionResolver
DispatcherServlet 默认装配的 HandlerExceptionResolver :
– 没有使用 mvc:annotation-driven/ 配置:
– 使用了 mvc:annotation-driven/ 配置:
ExceptionHandlerExceptionResolver
主要处理 Handler 中用 @ExceptionHandler 注解定义的方法。
@ExceptionHandler 注解定义的方法优先级问题:例如发生的是NullPointerException,但是声明的异常有 RuntimeException 和 Exception,此候会根据异常的最近继承关系找到继承深度最浅的那个 @ExceptionHandler 注解方法,即标记了 RuntimeException 的方法
ExceptionHandlerMethodResolver 内部若找不到@ExceptionHandler 注解的话,会找@ControllerAdvice 中的@ExceptionHandler 注解方法
ResponseStatusExceptionResolver
在异常及异常父类中找到 @ResponseStatus 注解,然后使用这个注解的属性进行处理。
定义一个 @ResponseStatus 注解修饰的异常类
若在处理器方法中抛出了上述异常:
若ExceptionHandlerExceptionResolver 不解析上述异常。由于触发的异常 UserNameNotMatchPasswordException 带有@ResponseStatus注解。因此会被ResponseStatusExceptionResolver 解析到。最后响应HttpStatus.FORBITDDEN 代码给客户端。 HttpStatus.FORBITDDEN 代表响应码403,禁止。关于其他的响应码请参考 HttpStatus 枚举类型源码。
DefaultHandlerExceptionResolver
对一些特殊的异常进行处理,比如NoSuchRequestHandlingMethodException、HttpReques
tMethodNotSupportedException、HttpMediaTypeNotSuppo rtedException、HttpMediaTypeNotAcceptableException
等。
SimpleMappingExceptionResolver
如果希望对所有异常进行统一处理,可以使用
SimpleMappingExceptionResolver,它将异常类名映射为
视图名,即发生异常时使用对应的视图报告异常
当个类的异常处理示例
注意:异常处理类要被Spring容器扫描到
注解写在方法上,捕获系统指定的异常类型,一旦该类其他方法执行时发生异常就执行对应的方法,就近原则
@RestController
public class ExceptionController {
@RequestMapping("/testEx")
public String testEx() {
int a = 1 / 0;
return "testEx";
}
/**
*
* @ExceptionHandler 注解写在方法上,捕获系统指定的异常类型,一旦该类其他方法执行时发生异常就执行对应的方法,就近原则
*/
@ExceptionHandler(Exception.class)
public String exception() {
return "exception";
}
@ExceptionHandler(RuntimeException.class)
public String runtimeException() {
return "runtimeException";
}
@ExceptionHandler(NullPointerException.class)
public String nullPointerException() {
return "nullPointerException";
}
@ExceptionHandler(ArithmeticException.class)
public String arithmeticException() {
return "ArithmeticException";
}
}
全局异常捕获
注意:异常处理类要被Spring容器扫描到
/**
* @ControllerAdvice +@ExceptionHandler实现全局异常捕获,系统其他类发生异常都会被捕获到
*/
//@ControllerAdvice
@RestControllerAdvice//@ResponseBody + @ControllerAdvice
public class GlobalException {
@RequestMapping("/exc")
public String exc() {
int a = 1 / 0;
return "exc";
}
@ExceptionHandler(Exception.class)
public String exception() {
return "exception";
}
@ExceptionHandler(RuntimeException.class)
public String runtimeException() {
return "runtimeException";
}
@ExceptionHandler(NullPointerException.class)
public String nullPointerException() {
return "nullPointerException";
}
@ExceptionHandler(ArithmeticException.class)
public String arithmeticException() {
return "ArithmeticException";
}
}
全局异常捕获(配置类的方式)
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<!--捕获所有的异常类型-->
<property name="exceptionAttribute" value="exception"/>
<!--根据exceptionAttribute得到的异常类型来做对应的处理-->
<property name="exceptionMappings">
<!--跳转到对应的页面-->
<props>
<prop key="java.lang.ArithmeticException">arithmeticException</prop>
<prop key="java.lang.RuntimeException">runtimeException</prop>
<prop key="java.lang.NullPointerException">nullPointerException</prop>
</props>
</property>
</bean>