SpringMVC核心技術
目錄
轉發與重定向
當處理器對請求處理完畢後,向其它資源進行跳轉時,有兩種跳轉方式:請求轉發與重定向。而根據所要跳轉的資源型別,又可分為兩類:跳轉到頁面與跳轉到其它處理器。
注意,對於請求轉發的頁面,可以是WEB-INF中頁面;而重定向的頁面,是不能為WEB-INF中的頁面。因為重定向相當於使用者再次發出一次請求,而使用者是不能直接訪問 WEB-INF 中的資源
SpringMVC 框架把原來 Servlet 中的請求轉發和重定向操作進行了封裝。現在可以使用簡單的方式實現轉發和重定向。
forward:表示轉發
redirect表示重定向
請求轉發
-
處理器方法返回 ModelAndView 時,需在 setViewName()指定的檢視前新增 forward
-
處理器方法返回 String,在檢視路徑前面加入 forward: ,轉發到檢視頁面。
-
轉發到其他處理器的格式:forward:xxx.do
@RequestMapping(value="/register.do") public ModelAndView doFirst(String name,int age) { System.out.println(name); ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("pname", name); modelAndView.addObject("page", age); //加上forward就是請求轉發 modelAndView.setViewName("forward:/WEB-INF/jsp/welcome.jsp"); return modelAndView; }
重定向
-
在處理器方法返回的檢視字串的前面新增 redirect:,則可實現重定向跳轉。
-
重定向ModelAndView,會將modelAndView.addObject(“key”, value);新增的資料當作請求引數傳遞
@RequestMapping(value="/register.do") public ModelAndView doFirst(String name,int age) { System.out.println(name); ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("pname", name); modelAndView.addObject("page", age); //加上redirect是重定向,重定向不能到WEB-INF裡的內容 //重定向會將addObject中新增的資料作為引數進行傳遞 //modelAndView.setViewName("redirect:/welcome.jsp?pname=name&page=age"); modelAndView.setViewName("redirect:/welcome.jsp"); return modelAndView; }
-
如何在重定向後獲取modelandView.addObject(“key”,value);中新增的資料
<body> <!-- param.pname底層呼叫的是request.getParameter(); //取出的資料是字串 requestScop:底層呼叫的是request.getAttribute(); //取出的資料帶型別 --> name = ${param.pname }<br> age = ${param.page } </body>
-
重定向到其他處理器,並且通過model攜帶資料(SpringBoot中使用RedirectAttributes)
@RequestMapping(value="/register.do") public String doFirst(String name,int age,Model model,HttpServletRequest request) throws UnsupportedEncodingException { request.setCharacterEncoding("UTF-8"); System.out.println(name); //通過model攜帶資料 //不能傳遞物件,因為是通過http協議傳輸 model.addAttribute("pname",name); model.addAttribute("page", age); return "redirect:/doother.do"; } @RequestMapping(value="/doother.do") public String doOther(String pname,int page) { System.out.println(pname); System.out.println(page); //轉發 return "/welcome.jsp"; }
異常處理器
定義異常處理器
-
@ExceptionHandler:用於捕獲所有控制器裡面的異常,並進行處理。
-
其他類繼承此異常處理器即可,也可以直接將異常處理方法寫到Controller中
@Controller //指定此類為控制器 public class BaseController { //處理NameException異常 @ExceptionHandler(NameException.class) public ModelAndView MyNameExceptionHandler(Exception exception) { ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("message", exception); System.out.println(exception.getMessage()); //預設跳轉NameError頁面 modelAndView.setViewName("/errors/NameError.jsp"); return modelAndView; } //處理AgeException異常 @ExceptionHandler(AgeException.class) public ModelAndView MyAgeExceptionHandler(Exception exception) { ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("message", exception); System.out.println(exception.getMessage()); //預設跳轉AgeError頁面 modelAndView.setViewName("/errors/AgeError.jsp"); return modelAndView; } //處理其他異常 @ExceptionHandler() public ModelAndView MyOtherExceptionHandler(Exception exception) { ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("message", exception); //預設跳轉AgeError頁面 modelAndView.setViewName("/errors/error.jsp"); return modelAndView; } }
型別轉換器
為什麼在表單中輸入"12",處理器方法可以以int型別接收引數? 這是因為框架內部幫我們做了型別轉換的工作。將String轉換成int,下面我們將定義型別轉換器,將表單提交的String轉換Date型別。
可通過ConversionServiceFactoryBean的converters屬性註冊自定義的型別轉換器
Converter<S,T>:將S型別物件轉為T型別物件
-
jsp頁面
<form action="${pageContext.request.contextPath }/test/myConverter.do"> 年齡:<input type="text" name="age"> 生日:<input type="text" name="birthday"> <input type="submit" value="提交"> </form>
-
Controller
@Controller //指定這個類是處理器 @RequestMapping(value="/test") //名稱空間 //以Date形式接收前端提交的引數birthday public class MyController extends MyExceptionHandler{ @RequestMapping(value="/myConverter.do") public ModelAndView dotypeC(int age,Date birthday) { ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("age", age).addObject("birthday", birthday); modelAndView.setViewName("/WEB-INF/jsp/welcome.jsp"); return modelAndView; } }
/* *Converter<String, Date> *引數1:源型別 *引數2:需要轉換的型別 */ public class myDateConverter implements Converter<String, Date> { @Override //source: 即為前端傳入的birthday public Date convert(String source) { //建立SimpleDateFormat通過getDateFromat(source)方法得到物件 SimpleDateFormat date = getDateFromat(source); //返回 date.parse(source) try { return date.parse(source); } catch (ParseException e) { e.printStackTrace(); } return null; } //此方法用於獲取SimpleDateFormat物件,通過正規表示式對source進行匹配 private SimpleDateFormat getDateFromat(String source) { SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); if (Pattern.matches("^\\d{4}-\\d{2}-\\d{2}$", source)) { simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); }else if (Pattern.matches("^\\d{4}/\\d{2}/\\d{2}$", source)) { simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd"); }else if (Pattern.matches("^\\d{4}\\d{2}\\d{2}$", source)) { simpleDateFormat = new SimpleDateFormat("yyyyMMdd"); }else if (Pattern.matches("^\\d{4}年\\d{2}月\\d{2}日$", source)) { simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日"); } return simpleDateFormat; } }
-
註冊型別轉換器
<!-- 註冊型別轉換器 --> <bean id="Converter" class="com.chuangmei.converter.myDateConverter" /> <!-- 註冊轉換服務工廠 --> <bean id="conversionServiceFactory" class="org.springframework.context.support.ConversionServiceFactoryBean"> <!-- <property name="converters" ref="Converter"> --> <property name="converters"> <set> <ref bean="Converter"/> </set> </property> </bean> <!-- 註冊mvc註解驅動 --> <mvc:annotation-driven conversion-service="conversionServiceFactory"/>
初始化引數繫結
-
Controller
@Controller //指定這個類是處理器 @RequestMapping(value="/test") //名稱空間 public class MyController { @RequestMapping(value="/myConverter.do") public ModelAndView dotypeC(int age,Date birthday) { ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("age", age).addObject("birthday", birthday); modelAndView.setViewName("/WEB-INF/jsp/welcome.jsp"); return modelAndView; } /* * 初始話引數繫結: * 在引數傳遞之前執行此方法 */ @InitBinder public void initBinder(WebDataBinder webDataBinder) { webDataBinder.registerCustomEditor(Date.class, new MyEditor()); } }
-
自定義編輯器
public class MyEditor extends PropertiesEditor { @Override public void setAsText(String source) throws IllegalArgumentException { // TODO Auto-generated method stub SimpleDateFormat simpleDateFormat = getDateFormat(source); try { Date date = simpleDateFormat.parse(source); setValue(date); } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private SimpleDateFormat getDateFormat(String source) { // TODO Auto-generated method stub SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); if (Pattern.matches("^\\d{4}-\\d{2}-\\d{2}$", source)) { simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); }else if (Pattern.matches("^\\d{4}/\\d{2}/\\d{2}$", source)) { simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd"); }else if (Pattern.matches("^\\d{4}\\d{2}\\d{2}$", source)) { simpleDateFormat = new SimpleDateFormat("yyyyMMdd"); }else { System.out.println("丟擲異常"); throw new TypeMismatchException("aaa", Integer.class); } return simpleDateFormat; } }
檔案上傳
-
jsp頁面
<form action="${pageContext.request.contextPath }/test/upload.do" method="post" enctype="multipart/form-data"> 檔案1:<input type="file" name="imgs"/><br> 檔案2:<input type="file" name="imgs"/><br> 檔案3:<input type="file" name="imgs"/><br> <input type="submit" value="上傳"/> </form>
-
Controller
@Controller //表示當前類為控制器 @RequestMapping("/test") //名稱空間 public class myController { @RequestMapping(value={"/upload.do"}) //這裡的引數名要與表單提交的引數名相同 //RequestParam:用於將指定的請求引數賦值給方法中的形參。 //value是表單的name名稱如果表單的name與方法形參相同,即可省略value public String doFileUpload(@RequestParam(value="imgs") MultipartFile[] imgs,HttpSession session) throws IllegalStateException, IOException { //建立目錄 /* * 這樣寫很不安全 * File filePath = new File("D:/images"); * */ //使用者只具有當前專案根下的操作許可權 String realPath = session.getServletContext().getRealPath("/images"); File filePath = new File(realPath); System.out.println(realPath); //如果不存在則建立 if (!filePath.exists()) { filePath.mkdirs(); } //遍歷MultipartFile陣列逐個判斷上傳 for (MultipartFile img : imgs) { //如果獲取到的位元組大於0,才執行上傳,不能用null判斷,因為即使前端不傳入引數,img也不會為null if(img.getSize()>0){ //獲取使用者上傳的檔案的檔名 String FileName = img.getOriginalFilename(); //限制上傳的檔案型別,只有字尾位jpg和png的才能上傳 if (FileName.endsWith("jpg") || FileName.endsWith("png")) { //指定檔案的上傳路徑 File file = new File(filePath, FileName); //上傳檔案 img.transferTo(file); }else { return "/message.jsp"; } } } return "/success.jsp"; } }
-
SpringMVC.xml
<!-- 檔案上傳 --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!-- 如果在上傳後,檔名出現亂碼 --> <property name="defaultEncoding" value="utf-8"/> <!-- 設定最大上傳位元組(b) 1kb = 1024b --> <property name="maxUploadSize" value="1048576"/> </bean> <!-- 註冊mvc註解驅動 --> <mvc:annotation-driven/>
攔截器
· SpringMVC 中的 Interceptor 攔截器的主要作用是攔截指定的使用者請求,並進行相應的預處理與後處理。其
·自定義攔截器,需要實現 HandlerInterceptor 介面。而該介面中含有三個方法:
-
preHandle(request, response, Object handler):該方法在處理器方法執行之前執行。其返回值為 boolean,若為 true,則緊接著會執行處理器方法,且會將 afterCompletion()方法放入到一個專門的方法棧中等待執行。
-
postHandle(request, response, Object handler, modelAndView):該方法在處理器方法執行之後執行。處理器方法若最終未被執行,則該方法不會執行。由於該方法是在處理器方法執行完後執行,且該方法引數中包含 ModelAndView,所以該方法可以修改處理器方法的處理結果資料,且可以修改跳轉方向。
-
afterCompletion(request, response, Object handler, Exception ex): 當 preHandle()方法返回 true 時,會將該方法放到專門的方法棧中,等到對請求進行響應的所有工作完成之後才執行該方法。即該方法是在中央排程器渲染(資料填充)了響應頁面之後執行的,此時對 ModelAndView 再操作也對響應無濟於事。
· 攔截器的使用
-
Controller
/* * 攔截器執行順序 * 1.preHandler * 2.myController * 3.postHandler * 4.afterCompletion */ @Controller //表示當前類是個控制器 @RequestMapping("/test") //名稱空間 public class myController { //提交*.do或/hello.do的請求 執行此方法 @RequestMapping("some.do") public String doFirst() { System.out.println("doSOme------------"); return "/WEB-INF/jsp/welcome.jsp"; } }
-
interceptor
/* * 攔截器執行順序 * 1.preHandler * 2.myController * 3.postHandler * 4.afterCompletion */ public class Oneinterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // TODO Auto-generated method stub System.out.println("Oneinterceptor -------------------- preHandle"); //此處返回false即表示不在往下執行 return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // TODO Auto-generated method stub System.out.println("Oneinterceptor -------------------- postHandle"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // TODO Auto-generated method stub System.out.println("Oneinterceptor -------------------- afterCompletion"); } }
-
springmvc.xml(註冊攔截器)
<!-- 註冊攔截器 --> <mvc:interceptors> <!-- Oneinterceptor --> <mvc:interceptor> <!-- 指定攔截什麼請求 --> <mvc:mapping path="/**"/> <!-- 攔截所有請求 --> <!-- 指定不攔截什麼請求 --> <!-- <mvc:exclude-mapping path=""/> --> <!-- 攔截器 --> <bean class="com.chuangmei.interceptor.Oneinterceptor"/> </mvc:interceptor> <!-- Twointerceptor --> <mvc:interceptor> <mvc:mapping path="/**"/> <bean class="com.chuangmei.interceptor.Twointerceptor"/> </mvc:interceptor> </mvc:interceptors>
· 原始碼解析
-
DispatcherServlet.doDispatch
-
找到處理器介面卡後,會執行這段程式碼
//在找到處理器介面卡後會執行到此處去呼叫HandlerExecutionChain的applyPreHandle(processedRequest, response) //如果 preHandle 方法返回false,那麼 applyPreHandle 也會返回false,最後就會執行return語句,程式將不再往下執行 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; }
-
HandlerExecutionChain.applyPreHandle
-
此方法是在找到處理器介面卡後由處理器執行鏈進行呼叫,此方法會讓 interceptor 去呼叫 preHandle 方法,返回值與 preHandle 相同
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception { //可能有多個攔截器,所以陣列的形式接收 HandlerInterceptor[] interceptors = this.getInterceptors(); //判斷攔截器陣列是否為空 if (!ObjectUtils.isEmpty(interceptors)) { //遍歷攔截器陣列 for(int i = 0; i < interceptors.length; this.interceptorIndex = i++) { //取出陣列中的攔截器 HandlerInterceptor interceptor = interceptors[i]; /*讓攔截器去執行我們實現 HandlerInterceptor 後重寫的 preHandle 方法 如果 preHandle 返回false,那麼此處也返回false,否則返回true */ if (!interceptor.preHandle(request, response, this.handler)) { //如果preHandle 返回false, 執行 triggerAfterCompletion this.triggerAfterCompletion(request, response, (Exception)null); return false; } } } return true; }
-
HandlerExecutionChain.applyPostHandle
-
在執行完 applyPreHandle 方法後,介面卡會呼叫 handle 方法去執行 HandlerExecutionChain 中的處理器,處理器執行結束後便會由 HandlerExecutionChain 去執行 applyPostHandle 方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception { //攔截器 HandlerInterceptor[] interceptors = this.getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { //倒敘遍歷 for(int i = interceptors.length - 1; i >= 0; --i) { HandlerInterceptor interceptor = interceptors[i]; //interceptor 執行 postHandle 方法 interceptor.postHandle(request, response, this.handler, mv); } } }
-
DispatcherServlet.processDispatchResult
-
執行完 applyPostHandle 方法後, 由 DispatcherServlet 去呼叫 processDispatchResult 方法,該方法會呼叫DispatcherServlet 的 render 方法, 在 DispatcherServlet 的 render 方法中會呼叫 ModelAndView 的 render 方法進行渲染資料
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,.....) { .... if (mv != null && !mv.wasCleared()) { //呼叫 DispatcherServlet 的 render 方法,將 mv 傳入 this.render(mv, request, response); } .... }
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception { .... //呼叫view物件自己的 render 方法渲染資料 view.render(mv.getModelInternal(), request, response); ... }
相關文章
- SpringMVC-09-Ajax技術SpringMVC
- Canvas 核心技術Canvas
- AJAX核心技術
- ## JavaSE核心技術Java
- 不重視技術,何談掌握核心技術?
- springMvc的核心SpringMVC
- SpringMVC 乾貨系列:從零搭建 SpringMVC+mybatis(四):Spring 兩大核心之 AOP 學習 | 掘金技術徵文SpringMVCMyBatis
- 交換技術:NGN核心軟交換技術分析(轉)
- Linux核心技術分析Linux
- Apache Flink核心技術Apache
- Spring Boot核心技術Spring Boot
- SpringMVC 乾貨系列:從零搭建 SpringMVC+mybatis(三):Spring 兩大核心之 IOC/DI 學習 | 掘金技術徵文SpringMVCMyBatis
- ShowMeBug 核心技術內幕
- Flutter核心技術與實戰Flutter
- PHP 核心技術 --物件導向PHP物件
- 區塊鏈的核心技術區塊鏈
- 人工智慧核心6技術人工智慧
- Transfer Learning 核心技術研修
- Redis核心技術31-35Redis
- Redis核心技術26-30Redis
- IT核心技術與我,曾有交集
- JavaEE的13種核心技術Java
- 隱私計算核心技術
- 雙核心瀏覽器核心切換控制技術瀏覽器
- 如何掌握C#的核心技術C#
- Elasticsearch核心技術(二):Elasticsearch入門Elasticsearch
- Java核心技術筆記 繼承Java筆記繼承
- 智慧雲解析有哪些核心技術?
- Elasticsearch核心技術(四):索引原理分析Elasticsearch索引
- Java核心技術點之集合框架Java框架
- 區塊鏈核心技術概覽區塊鏈
- MySQL核心技術之“WHERE條件”MySql
- Face++官網核心技術整理
- Docker 核心技術與實現原理Docker
- Bing搜尋核心技術BitFunnel原理
- canvas核心技術-如何繪製圖形Canvas
- canvas核心技術-如何繪製線段Canvas
- JAVA核心技術學習筆記--反射Java筆記反射