目錄
- 異常處理
- 類型轉換器
- 數據驗證
- 文件上傳與下載
- 攔截器
異常處理
Spring MVC中, 系統的DAO, Service, Controller層出現異常, 均通過throw Exception向上拋出,
最后由中央處理器DispatchServlet交由全局異常處理器進行異常處理, 如下圖所示
常用的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方法可以將文件存儲到磁盤.
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修改也不會影響到頁面結果.
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()方法會執行.
實際運用
- 權限校驗: 判斷當前請求是否登陸, 若未登錄或者無權限, 返回對應的界面.
- 性能監控: 監控系統指定方法執行的時間, 尋找系統性能調優點.
- 日志打印: 記錄請求日志, 用於系統監控, 信息統計與分析等等.
- ......