5. SpringMVC的高级开发
5.1 异常处理器
springmvc在处理请求过程中出现异常信息交由异常处理器进行处理,自定义异常处理器可以实现一个系统的异常处理逻辑。
思路:
系统中异常包括两类:预期异常和运行时异常RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。
系统的dao、service、controller出现都通过throws Exception向上抛出,最后由springmvc前端控制器交由异常处理器进行异常处理,如下图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ly5zm9rR-1671171663800)(assets/wps1.jpg)]
为了区别不同的异常通常根据异常类型自定义异常类,这里我们创建一个自定义系统异常,如果controller、service、dao抛出此类异常说明是系统预期处理的异常信息。
package com.suke.exception;
public class UserException extends Exception {
public UserException() {
}
public UserException(String message) {
super(message);
}
public UserException(String message, Throwable cause) {
super(message, cause);
}
public UserException(Throwable cause) {
super(cause);
}
public UserException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
系统遇到异常,在程序中手动抛出,dao抛给service、service给controller、controller抛给前端控制器,前端控制器调用全局异常处理器。
全局异常处理器处理思路:
解析出异常类型
如果该 异常类型是系统 自定义的异常,直接取出异常信息,在错误页面展示
如果该 异常类型不是系统 自定义的异常,构造一个自定义的异常类型(信息为“未知错误”)
springmvc提供一个HandlerExceptionResolver接口
public class GlobalExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) {
CustomerException cex = null;
if(ex instanceof CustomerException){
//如果抛出的是CustomerException,则直接转换
cex = (CustomerException) ex;
}else{
//如果抛出的不是CustomerException异常,则构造一个CustomerException异常,异常信息为"未知错误"
cex = new CustomerException("未知错误");
}
//把错误信息保存到ModelAndView
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("msg", cex.getMessage());
//设置错误页面
modelAndView.setViewName("error");
return modelAndView;
}
}
全局异常处理器配置:
在springmvc.xml中添加:
<!-- 全局异常处理器:只要实现HandlerExceptionResolver接口的,都是全局异常处理器-->
<bean class="com.suke.exception.GlobalExceptionResolver"></bean>
注意:
如果与业务功能相关的异常,建议在service中抛出异常。
与业务功能没有关系的异常,建议在controller中抛出。
5.2 文件上传
文件上传客户端三要素
表单项type=“file”
表单的提交方式是post
表单的enctype属性是多部分表单形式,及enctype=“multipart/form-data”
<form action="${pageContext.request.contextPath}/testUpload1" method="post"
enctype="multipart/form-data">
名称:<input type="text" name="name"><br>
文件:<input type="file" name="file"><br>
<input type="submit" value="提交"><br>
</form>
单文件上传步骤
① 导入fileupload和io依赖
<dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.4</version> </dependency>
② 配置文件上传解析器
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!--上传文件总大小--> <property name="maxUploadSize" value="5242800"/> <!--上传单个文件的大小--> <property name="maxUploadSizePerFile" value="5242800"/> <!--上传文件的编码类型--> <property name="defaultEncoding" value="UTF-8"/> </bean>
注意:bean的id一定为multipartResolver,否则获取不到数据
③ 编写文件上传代码
@RequestMapping("/testUpload1") @ResponseBody public void testUpload1(String name,MultipartFile file) throws IOException { if(file != null){ //获得上传文件名文件名称 String originalFilename = uploadFile.getOriginalFilename(); String savePath="D:/upload/"; String suffix = originalFilename.substring(originalFilename.lastIndexOf(".")); File saveFile = new File(savePath,new Date().getTime()+suffix); //保存文件 uploadFile.transferTo(saveFile); } return "index"; }
在Idea配置虚拟路径
把服务器本地的一个目录, 部署到tomcat, 绑定一个url, 这个url映射是服务器本地的一个目录,
并把url暴露出来, 用户通过网络访问这个url, tomcat根据配置的, 自动找对应服务器本地的一个目录
5.3 拦截器
Spring MVC 的拦截器类似于 Servlet 开发中的过滤器 Filter,用于对处理器进行预处理和后处理。将拦截器按一定的顺序联结成一条链,这条链称为拦截器链(Interceptor Chain)。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。拦截器也是AOP思想的具体实现。
拦截器和过滤器区别
自定义拦截器很简单,只有如下三步:
① 创建拦截器类实现HandlerInterceptor接口
public class HandlerInterceptor1 implements HandlerInterceptor {
/**
* controller执行前调用此方法
* 返回true表示继续执行,返回false中止执行
* 这里可以加入登录校验、权限拦截等
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse
response, Object handler) {
System.out.println("preHandle running...");
return true;
}
/**
* controller执行后但未返回视图前调用此方法
* 这里可在返回用户前对模型数据进行加工处理,比如这里加入公用信息以便页面显示
*/
public void postHandle(HttpServletRequest request, HttpServletResponse
response, Object handler, ModelAndView modelAndView) {
System.out.println("postHandle running...");
}
/**
* controller执行后且视图返回后调用此方法
* 这里可得到执行controller时的异常信息
* 这里可记录操作日志,资源清理等
*/
public void afterCompletion(HttpServletRequest request, HttpServletResponse
response, Object handler, Exception ex) {
System.out.println("afterCompletion running...");
}
}
② 配置拦截器
- 针对某种mapping配置拦截器
**springmvc拦截器针对HandlerMapping进行拦截设置,**如果在某个HandlerMapping中配置拦截,经过该 HandlerMapping映射成功的handler最终使用该拦截器。
<bean
class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
<property name="interceptors">
<list>
<ref bean="handlerInterceptor1"/>
</list>
</property>
</bean>
<bean id="handlerInterceptor1"class="springmvc.intercapter.HandlerInterceptor1"/>
-
针对所有mapping配置全局拦截器
springmvc配置类似全局的拦截器,springmvc框架将配置的类似全局的拦截器注入到每个HandlerMapping中。
<!--拦截器 --> <mvc:interceptors> <!--多个拦截器,顺序执行 --> <mvc:interceptor> <!—/**拦截所有的url包括子url --> <mvc:mapping path="/**"/> <bean class="com.suke.interceptor.HandlerInterceptor1"></bean> </mvc:interceptor> </mvc:interceptors>
③ 测试拦截器的拦截效果
1. 两个拦截器都放行
HandlerInterceptor1...preHandle HandlerInterceptor2...preHandle HandlerInterceptor2...postHandle HandlerInterceptor1...postHandle HandlerInterceptor2...afterCompletion HandlerInterceptor1...afterCompletion
总结:
preHandle方法按顺序执行,
postHandle和afterCompletion按拦截器配置的逆向顺序执行。
2. 拦截器1放行,拦截器2不放行
HandlerInterceptor1...preHandle HandlerInterceptor2...preHandle HandlerInterceptor1...afterCompletion
总结:
拦截器1放行,拦截器2 preHandle才会执行。
拦截器2 preHandle不放行,拦截器2 postHandle和afterCompletion不会执行。
只要有一个拦截器不放行,postHandle不会执行。
- 拦截器1不放行,拦截器2不放行
HandlerInterceptor1...preHandle
总结:
拦截器1 preHandle不放行,postHandle和afterCompletion不会执行。
拦截器1 preHandle不放行,拦截器2不执行。
- 小结
preHandle按拦截器定义顺序调用
postHandler按拦截器定义逆序调用
afterCompletion按拦截器定义逆序调用
postHandler在拦截器链内所有拦截器返成功调用
afterCompletion只有preHandle返回true才调用
w) {
System.out.println(“postHandle running…”);
}
/**
* controller执行后且视图返回后调用此方法
* 这里可得到执行controller时的异常信息
* 这里可记录操作日志,资源清理等
*/
public void afterCompletion(HttpServletRequest request, HttpServletResponse
response, Object handler, Exception ex) {
System.out.println(“afterCompletion running…”);
}
}
② 配置拦截器
- **针对某种mapping配置拦截器**
**springmvc拦截器针对HandlerMapping进行拦截设置,**如果在某个HandlerMapping中配置拦截,经过该 HandlerMapping映射成功的handler最终使用该拦截器。
```xml
<bean
class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
<property name="interceptors">
<list>
<ref bean="handlerInterceptor1"/>
</list>
</property>
</bean>
<bean id="handlerInterceptor1"class="springmvc.intercapter.HandlerInterceptor1"/>
-
针对所有mapping配置全局拦截器
springmvc配置类似全局的拦截器,springmvc框架将配置的类似全局的拦截器注入到每个HandlerMapping中。
<!--拦截器 --> <mvc:interceptors> <!--多个拦截器,顺序执行 --> <mvc:interceptor> <!—/**拦截所有的url包括子url --> <mvc:mapping path="/**"/> <bean class="com.suke.interceptor.HandlerInterceptor1"></bean> </mvc:interceptor> </mvc:interceptors>
③ 测试拦截器的拦截效果
1. 两个拦截器都放行
HandlerInterceptor1...preHandle HandlerInterceptor2...preHandle HandlerInterceptor2...postHandle HandlerInterceptor1...postHandle HandlerInterceptor2...afterCompletion HandlerInterceptor1...afterCompletion
总结:
preHandle方法按顺序执行,
postHandle和afterCompletion按拦截器配置的逆向顺序执行。
2. 拦截器1放行,拦截器2不放行
HandlerInterceptor1...preHandle HandlerInterceptor2...preHandle HandlerInterceptor1...afterCompletion
总结:
拦截器1放行,拦截器2 preHandle才会执行。
拦截器2 preHandle不放行,拦截器2 postHandle和afterCompletion不会执行。
只要有一个拦截器不放行,postHandle不会执行。
- 拦截器1不放行,拦截器2不放行
HandlerInterceptor1...preHandle
总结:
拦截器1 preHandle不放行,postHandle和afterCompletion不会执行。
拦截器1 preHandle不放行,拦截器2不执行。
- 小结
preHandle按拦截器定义顺序调用
postHandler按拦截器定义逆序调用
afterCompletion按拦截器定义逆序调用
postHandler在拦截器链内所有拦截器返成功调用
afterCompletion只有preHandle返回true才调用