Spring MVC核心技术 超、凢脫俗 2022-05-28 04:00 198阅读 0赞 ### 目录 ### * **异常处理** * **类型转换器** * **数据验证** * **文件上传与下载** * **拦截器** -------------------- ### 异常处理 ### Spring MVC中, 系统的DAO, Service, Controller层出现异常, 均通过throw Exception向上抛出, 最后由中央处理器DispatchServlet交由全局异常处理器进行异常处理, 如下图所示 ![1285727-20180328234146442-744453865.png][] 常用的Spring MVC异常处理方式主要有三种: * 使用系统定义好的异常处理器SimpleMappingExceptionResolver * 使用自定义异常处理器 * 使用异常注解 **SimpleMappingExceptionResolver** 只需要在配置文件中注册该异常处理器Bean即可, 无需显式调用, 当异常发生时会自动执行该类. 1 <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> 2 <property name="defaultErrorView" value="/errors/error.jsp"/> 3 </bean> (1) 声明一个自定义的异常类 1 public class NameException extends Exception { 2 3 public NameException() { 4 super(); 5 } 6 7 public NameException(String message) { 8 super(message); 9 } 10 11 } (2) 注册异常处理器 1 <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> 2 <property name="defaultErrorView" value="/errors/error.jsp"/> 3 <property name="exceptionAttribute" value="ex"/> 4 <property name="exceptionMappings"> 5 <props> 6 <prop key="com.test.exceptions.NameException">/errors/nameError.jsp</prop> 7 </props> 8 </property> 9 </bean> exceptionMapping: Properties类型属性, 用于指定具体的不同类型异常对应的响应页面. defaultErrorView: 默认的异常响应页面, 若发生的异常不是自定义的异常, 则使用默认响应页面. exceptionAttribute: 捕获到的异常对象, 一般异常响应页面中使用. **自定义异常处理器** SimpleMappingExceptionResolver可以在发生异常的时候进行页面跳转, 但是如果想要在捕获到特定异常后, 执行一些操作, 则需要自定义异常. 自定义异常处理器需要实现HandlerExceptionResolver接口, 并且该类 需要在配置文件中进行注册. (1) 定义异常处理器 当一个类实现了HandlerExceptionResolver接口后, 只要有异常发生, 都会执行resolveException方法. 1 public class MyHandlerExceptionResolver implements HandlerExceptionResolver { 2 3 public ModelAndView resolveException(HttpServletRequest request, 4 HttpServletResponse response, Object handler, Exception ex) { 5 6 ModelAndView mv = new ModelAndView(); 7 mv.addObject("ex", ex); 8 mv.setViewName("/errors/error.jsp"); 9 10 if(ex instanceof NameException) { 11 // 执行一些操作 12 mv.setViewName("/errors/nameError.jsp"); 13 } 14 15 return mv; 16 } 17 18 } (2) 注册异常处理器 1 <!-- 注册异常处理器 --> 2 <bean class="com.test.resolvers.MyHandlerExceptionResolver"/> **异常处理注解** 使用注解@ExceptionHandler可以将一个方法指定为异常处理方法, 该注解有一个可选属性value, 可用于指定该注解方法所需要处理的异常类. 定义一个顶层Controller, 处理所有异常, 其他Controller继承该类即可实现异常集中管理. 1 @Controller 2 public class BaseController { 3 4 // 处理NameException异常 5 @ExceptionHandler(NameException.class) 6 public ModelAndView handlerNameException(Exception ex) { 7 ModelAndView mv = new ModelAndView(); 8 mv.addObject("ex", ex); 9 // 执行一些操作 10 mv.setViewName("/errors/nameError.jsp"); 11 return mv; 12 } 13 14 } ### 类型转换器 ### 可以将用户web端提交的数据, 在后台转为需要的数据类型. **自定义类型转换器** 若要定义类型转换器, 则需要实现Converter接口, 该Convert接口有两个泛型: 第一个为待转换类型, 第二个为目标类型, 该接口方法convert用于实现转换. 1 public class MyDateConverter implements Converter<String, Date> { 2 3 public Date convert(String source) { 4 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); 5 try { 6 return sdf.parse(source); 7 } catch (ParseException e) { 8 e.printStackTrace(); 9 } 10 return null; 11 } 12 13 } 类型转换器定义完毕后, 需要在配置文件中进行注册, 然后注册一个转换服务Bean, 将转换器注入给该Bean, 最后由处理器适配器来使用该转换服务器Bean. 该Bean由ConversionServiceFactory工厂创建, 工厂有Set集合属性, 可以提供多种转换功能的Bean来处理多种数据类型转换. 1 <!-- 注册类型转换器 --> 2 <bean id="myDateConverter" class="com.test.converters.MyDateConverter"/> 3 4 <!-- 注册转换服务对象 --> 5 <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"> 6 <property name="converters" ref="myDateConverter"/> 7 </bean> 8 9 <!-- 注册mvc注解驱动 --> 10 <mvc:annotation-driven conversion-service="conversionService"/> ### 数据验证 ### 主要是校验客户端发来的数据是否合法, 例如不能为空, 或者长度不符合等等. Spring MVC没有校验功能, 但是支持JSR303-Bean Validation, 可以用实现该规范的Hibernate Validator校验框架. (1) 配置验证器 1 <!-- 生成验证器 --> 2 <bean id="myValidator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"> 3 <property name="providerClass" value="org.hibernate.validator.HibernateValidator"/> 4 </bean> (2) 在Bean上添加验证注解 1 public class Student { 2 3 @NotNull(message="姓名不能为空") 4 @Size(min=3, max=6, message="姓名长度应在{min}-{max}个字符") 5 private String name; 6 7 @Min(value=0, message="成绩不能小于{value}") 8 @Max(value=100, message="成绩不能大于{value}") 9 private double score; 10 11 12 @NotNull(message="电话不能为空") 13 @Pattern(regexp="^1[34578]\\d{9}$", message="手机号格式不正确") 14 private String mobile; 15 16 ...... 17 } (3) 修改Controller 在需要校验的参数前面加上@Validated注解, 同时追加一个BindingResult参数, 用于获取验证异常信息. 1 @Controller 2 @RequestMapping("/test") 3 public class MyController { 4 5 @RequestMapping("/register.do") 6 public ModelAndView doRegister(@Validated Student student, BindingResult br) { 7 8 ModelAndView mv = new ModelAndView(); 9 mv.addObject("student", student); 10 mv.setViewName("/WEB-INF/jsp/welcome.jsp"); 11 12 int errorCount = br.getErrorCount(); 13 if(errorCount > 0) { 14 FieldError nameError = br.getFieldError("name"); 15 16 if (nameError != null) { 17 String nameErrorMSG = nameError.getDefaultMessage(); 18 mv.addObject("nameErrorMSG", nameErrorMSG); 19 } 20 mv.setViewName("/index.jsp"); 21 } 22 23 return mv; 24 } 25 } ### 文件上传与下载 ### **上传** Spring MVC中文件上传需要添加Apache Commons FileUpload相关的jar包, 基于该jar, Spring中提供了MultipartResolver实现类: CommonsMultipartResolver. 注册该Bean到配置文件中 <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!-- 上传文件大小上限,单位为字节(10MB) --> <property name="maxUploadSize"> <value>10485760</value> </property> <!-- 请求的编码格式,必须和jSP的pageEncoding属性一致,以便正确读取表单的内容,默认为ISO-8859-1 --> <property name="defaultEncoding"> <value>UTF-8</value> </property> </bean> Spring MVC会将上传的文件绑定到MultipartFile对象, 该对象提供获取内容, 文件名等方法. 通过transferTo方法可以将文件存储到磁盘. ![1285727-20180329002634028-1239331494.jpg][] 1 //上传文件会自动绑定到MultipartFile中 2 @RequestMapping(value="/upload",method=RequestMethod.POST) 3 public String upload(HttpServletRequest request, 4 @RequestParam("description") String description, 5 @RequestParam("file") MultipartFile file) throws Exception { 6 7 System.out.println(description); 8 //如果文件不为空,写入上传路径 9 if(!file.isEmpty()) { 10 //上传文件路径 11 String path = request.getServletContext().getRealPath("/images/"); 12 //上传文件名 13 String filename = file.getOriginalFilename(); 14 File filepath = new File(path,filename); 15 //判断路径是否存在,如果不存在就创建一个 16 if (!filepath.getParentFile().exists()) { 17 filepath.getParentFile().mkdirs(); 18 } 19 //将上传文件保存到一个目标文件当中 20 file.transferTo(new File(path + File.separator + filename)); 21 return "success"; 22 } else { 23 return "error"; 24 } 25 26 } **下载** 下载比较简单, Spring MVC中提供了ResponseEntity类型, 可以很方便的返回HTTPHeaders, HttpStatus. @RequestMapping(value="/download") public ResponseEntity<byte[]> download(HttpServletRequest request, @RequestParam("filename") String filename, Model model)throws Exception { //下载文件路径 String path = request.getServletContext().getRealPath("/images/"); File file = new File(path + File.separator + filename); HttpHeaders headers = new HttpHeaders(); //下载显示的文件名,解决中文名称乱码问题 String downloadFielName = new String(filename.getBytes("UTF-8"),"iso-8859-1"); //通知浏览器以attachment(下载方式)打开图片 headers.setContentDispositionFormData("attachment", downloadFielName); //application/octet-stream : 二进制流数据(最常见的文件下载)。 headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); return new ResponseEntity<byte[]>(FileUtils.readFileToByteArray(file), headers, HttpStatus.CREATED); } ### 拦截器 ### **拦截器注册与使用** Spring MVC中拦截器需要实现HandlerInterceptor接口, 该接口包含三个方法 **preHandle(req, res, handler)** 在处理器方法之前执行, 返回boolean, 若为true, 则紧接着执行处理器方法, 且会将 afterCompletion()方法放入一个专门的方法栈中等待执行. **postHandler(req, res, handler, ModelAndView)** 在处理器方法之后执行, 处理器方法若未执行, 则该方法不执行. 该方法可以修改处理器方法返回的结果, 并可以跳转到其他页面. afterCompletion(req, res, handler, exception) 当preHandle返回ture后, 会将该方法放入等待区, 等所有请求响应结束, 执行该方法. 即中央处理器渲染了响应页面之后执行, 此时对ModelAndView修改也不会影响到页面结果. ![1285727-20180329003905947-1533470515.jpg][] 1 public class OneInterceptor implements HandlerInterceptor { 2 3 public boolean preHandle(HttpServletRequest request, 4 HttpServletResponse response, Object handler) throws Exception { 5 System.out.println("执行OneIntercepor ---- preHandle() ------"); 6 return false; 7 } 8 9 public void postHandle(HttpServletRequest request, 10 HttpServletResponse response, Object handler, 11 ModelAndView modelAndView) throws Exception { 12 System.out.println("执行OneIntercepor ---- postHandle() ------"); 13 } 14 15 public void afterCompletion(HttpServletRequest request, 16 HttpServletResponse response, Object handler, Exception ex) 17 throws Exception { 18 System.out.println("执行OneIntercepor ---- afterCompletion() ------"); 19 } 20 21 } 在配置文件中注册 1 <!-- 注册拦截器 --> 2 <mvc:interceptors> 3 <mvc:interceptor> 4 <mvc:mapping path="/**"/> 5 <bean class="com.test.interceptors.OneInterceptor"/> 6 </mvc:interceptor> 7 </mvc:interceptors> <mvc:mapping/>用于指定当前拦截器拦截的请求路径, /\*\*表示拦截所有请求. **多个拦截器** 当有多个拦截器时, 形成拦截器链, 拦截器链执行顺序与注册顺序一致, 需要注意的是, 当一个拦截器preHandle()返回false时, 上部的拦截器链将被断开, 后续的处理器以及对于的postHandle() 都无法执行, 只有已经执行通过的拦截器的afterCompletion()方法会执行. ![1285727-20180329004641364-1020477892.jpg][] **实际运用** * 权限校验: 判断当前请求是否登陆, 若未登录或者无权限, 返回对应的界面. * 性能监控: 监控系统指定方法执行的时间, 寻找系统性能调优点. * 日志打印: 记录请求日志, 用于系统监控, 信息统计与分析等等. * ...... 我有一个微信公众号,经常会分享一些Java技术相关的干货;如果你喜欢我的分享,可以用微信搜索“Java团长”或者“javatuanzhang”关注。 [1285727-20180328234146442-744453865.png]: /images/20220528/f9a9bd8603814a0083c0172515ff3caa.png [1285727-20180329002634028-1239331494.jpg]: /images/20220528/5af82c1c60bc4f42893574ff2b7686d5.png [1285727-20180329003905947-1533470515.jpg]: /images/20220528/f2694fa57de94fdb84bd23874056012f.png [1285727-20180329004641364-1020477892.jpg]: /images/20220528/43f11f62c18940c7b96af92904d4ab77.png
还没有评论,来说两句吧...