SpringMVC 之拦截器和异常处理
1. 文件上传
- Spring 使用 Jakarta Commons FileUpload 技术实现了一个 MultipartResolver 实现类:
CommonsMultipartResolver
; - SpringMVC 上下文中默认没有装配 MultipartResolver,因此默认情况下不能处理文件的上传;
若要使用上传功能,需要在上下文中配置MultipartResolver
;
// 1. 导入 jar 包
/* commons-fileupload;
* commons-io;
*/
// 2. 配置 MultipartResolver(多媒体解析器)
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="UTF-8"></property>
<!-- 文件大小 1M -->
<property name="maxUploadSize" value="1048576"/>
</bean>
// index.jsp
<h2>文件上传</h2>
<form action="${pageContext.request.contextPath}/testUpload" method="post"
enctype="multipart/form-data">
file1:<input type="file" name="upload"/><br/>
file2:<input type="file" name="upload"/><br/>
file3:<input type="file" name="upload"/><br/>
<input type="submit" value="上传"/><br/>
</form>
// Demo.java
@RequestMapping(value="/testUpload",method=RequestMethod.POST)
public String testUpload(@RequestParam("upload") MultipartFile[] file)
throws IllegalStateException, IOException {
for(MultipartFile mf : file){
if(!mf.isEmpty()){
mf.transferTo(new File("/Users/用户名/Documents/上传/"+
mf.getOriginalFilename()));
}
}
return "ok";
}
2. 自定义拦截器
- 自定义的拦截器必须实现 HandlerInterceptor 接口:
preHandle()
:这个方法在业务处理器处理请求之前被调用,在该方法中对用户请求 request 进行
处理;如果拦截处理后,还需要调用其他的拦截器,或者是业务处理器,则返回 true; 否则,返回false;postHandle()
:这个方法在业务处理器处理完请求后,但是,DispatcherServlet 向客户端返回
响应前被调用,处理用户的 request请求;afterCompletion()
:这个方法在 DispatcherServlet 完全处理完请求后被调用,可以在该方
法中进行一些资源清理的操作;
// FirstInterceptor.java
public class FirstInterceptor implements HandlerInterceptor{
public boolean preHandle(HttpServletRequest req,HttpServletResponse resp,
Object handler)throws Exception{
System.out.println("FirstInterceptor.....preHandle");
return true;
}
public void postHandle(HttpServletRequest req, HttpServletResponse resp,
Object handler, ModelAndView modelAndView)throws Exception{
System.out.println("FirstInterceptor.....postHandle");
}
public void afterCompletion(HttpServletRequest req,HttpServletResponse resp,
Object handler,Exception ex) throws Exception{
System.out.println("FirstInterceptor.....afterCompletion");
}
}
// 注册该拦截器
<mvc:interceptors>
<bean id="firstInterceptor"
class="cn.itcast.springmvc.interceptor.firstInterceptor"/>
</mvc:interceptors>
// index.jsp
示例:<a href="${pageContext.request.contextPath}/helloworld">点击这里</a>
// Demo.java
@Controller
public class Demo{
@RequestMapping(value="/hellowrold",method=RequestMethod.GET)
public String helloworld(){
System.out.println("======helloworld");
return "ok";
}
}
运行结果:
2.1 拦截器配置
// 自定义两个拦截器
// FirstInterceptor.java(同上)
// SecondInterceptor.java
public class SecondInterceptor implements HandlerInterceptor{
public boolean preHandle(HttpServletRequest req,HttpServletResponse resp,
Object handler)throws Exception{
System.out.println("SecondInterceptor.....preHandle");
return true;
}
public void postHandle(HttpServletRequest req, HttpServletResponse resp,
Object handler, ModelAndView modelAndView)throws Exception{
System.out.println("SecondInterceptor.....postHandle");
}
public void afterCompletion(HttpServletRequest req,HttpServletResponse resp,
Object handler,Exception ex) throws Exception{
System.out.println("SecondInterceptor.....afterCompletion");
}
}
// 注册拦截器
<mvc:interceptors>
<bean id="firstInterceptor"
class="cn.itcast.springmvc.interceptor.FirstInterceptor"/>
<mvc:interceptor>
<mvc:mapping path="/emps"></mvc:mapping>
<bean id="secondInterceptor"
class="cn.itcast.springmvc.interceptor.SecondInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
// Demo.java
@Controller
public class Demo{
@RequestMapping(value="/emps",method=RequestMethod.GET)
public String list(Map<String,Object> map){
System.out.println("emps .... 执行");
map.put("employees",employeeService.getAll());
return "list";
}
}
// index.jsp
查询所有员工: <a href="${pageContext.request.contextPath}/emps">查询所有</a>
结果分析
- 第一个拦截器
preHandler
返回 true,第二个也为 true;
- 第一个拦截器返回 false,第二个为true或false;
- 第一个拦截器返回 true,第二个为 false;
3. 异常处理
- SpringMVC 通过
HandlerExceptionResolver
接口处理程序的异常,包括Handler映射,数据绑定以
及目标方法执行时,发生的异常; - SpringMVC 提供的
HandlerExceptionResolver
接口的实现类:ExceptionHandlerExceptionResolver
;DefaultHandlerExceptionResolver
;ResponseStatusExdeptionResolver
;SimpleMappingExceptionResolver
;
3.1 HandlerExceptionResolver
@ExceptionHandler
只处理当前 handler 方法中抛出的异常
// index.jsp
异常示例:<a href="{pageContext.request.contextPath}/testHandler?age=5">异常处理1</a>
// Demo.java
@Controller
public class Demo{
@RequestMapping(value="/testHandler",method=RequestMethod.GET)
public String testHandler(@RequestParam("age") Integer age){
int result = 10 / age;
System.out.println(result);
return "ok";
}
// 如果请求参数 age=0, 会报异常
// error.jsp
<body>
抱歉,系统繁忙,请稍候在试....
</body>
// 第一种方式: 返回到错误页面,不带异常信息
@ExceptionHandler(value={ArithmeticException.class})
public String dealHandlerExceptionResolver(Exception e){
System.out.println("111111"+e.getMessage());
return "error";
}
// 第二种方式: 将异常信息返回到错误页面, 需要使用 ModelAndView, 不能使用 Map
@ExceptionHandler(value={ArithmeticException.class})
public ModelAndView dealHandlerExceptionResolver(Excetption e){
System.out.println("22222"+e.getMessage());
ModelAndView mv = new ModelAndView();
mv.addObject("myexception",e.getMessage());
mv.setViewName("error");
return mv;
}
// error.jsp
<body>
抱歉,系统繁忙,请稍候在试....<br/>
异常信息:${requestScope.myexception}
</body>
// 异常优先级问题
// ArithmeticExcetion 继承了 RuntimeException
// 如果一个类中既有ArithmeticException, 也有 RuntimeException
// 如果出现 ArithmeticException, 会执行ArithmeticException
@ExceptionHandler(value={RuntimeException.class})
public ModelAndView dealHandlerExceptionResolver2(Excetption e){
System.out.println("33333"+e.getMessage());
ModelAndView mv = new ModelAndView();
mv.addObject("myexception",e.getMessage());
mv.setViewName("error");
return mv;
}
}
3.2 @ControllerAdvice
- 将所有异常存放在
exception
包下,将业务方法和处理异常的方法分离; - @ExceptionHandler中找不到的话,就去
@ControllerAdvice
标记的类里面查找标记了
@ExceptionHandler的方法;
// cn.itcast.springmvc.exception.CommonHelperException 类
@ControllerAdvice
public class CommonHelperException{
@ExceptionHandler(value={ArithmeticException.class})
public ModelAndView dealHandlerExceptionResolver(Excetption e){
System.out.println("44444"+e.getMessage());
ModelAndView mv = new ModelAndView();
mv.addObject("myexception",e.getMessage());
mv.setViewName("error");
return mv;
}
}
3.3 ReponseStatusExceptionResolver
// 模拟账户锁定,自定义一个 MyUserLockException 继承了 RuntimeException 的异常类
// index.jsp
账户锁定异常:
<a href="${pageContext.request.contextPath}/testResponse?username=lisi">异常示例</a>
// cn.itcast.springmvc.exception.MyUserLockException 类
@ResponseStatus(value=HttpStatus.LOCKED,reason="账户被锁定,请拨打10086")
public class MyUserLockException extends RuntimeException{
private static final long serialVersionUID = 1L;
}
// Demo.java
@Controller
public class Demo{
@RequestMapping(value="/testResponse",method=RequestMethod.GET)
public String testResponse(@RequestParam("username") String username){
// username=zhangsan, 抛出异常
if("zhangsan".equalsIgnoreCase(username)){
throw new MyUserLockException();
}
return "ok";
}
//第二种用法,@ResponseStatus 用在方法上
// 如果 name=zhangsan, 报 MyUserLockException,
// 如果 name 为其他,报 Not Found
@RequestMapping(value="/testResponse",method=RequestMethod.GET)
@ResponseStatus(value=HttpStatus.NOT_FOUND,reason="测试...")
public String testResponse(@RequestParam("username") String username){
// username=zhangsan, 抛出异常
if("zhangsan".equalsIgnoreCase(username)){
throw new MyUserLockException();
}
return "ok";
}
}
3.4 SimpleMappingExceptionResolver
// 示例: 数组下标越界异常
// springDispatcherServlet-servlet.xml 配置
<bean id="simpleMappingExceptionResolver"
class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<!-- 可以自定义 requestScope.异常名称 --
<property name="exceptionAttribute" value="自定义异常名称"/>
<property name="exceptionMappings">
<props>
<prop key="java.lang.ArrayIndexOutOfBoundsException">error</prop>
</props>
</property>
</bean>
// index.jsp
异常示例:<a href="${pageContext.request.contextPath}/testSimple?id=3">数组下标越界</a>
// error.jsp
<body>
抱歉,系统繁忙,请稍候在试!<br/>
数组下标越界:${requestScope.exception}<br/>
</body>
// Demo.java
@Controller
public class Demo{
@RequestMapping(value="/testSimple",method=RequestMethod.GET)
public String testSimple(@RequestParam("id") Integer id){
int[] arr = new int[10];
System.out.println(arr[id]);
return "ok";
}
}
参考资料