spring(7)spring mvc 的高级技术
生活随笔
收集整理的這篇文章主要介紹了
spring(7)spring mvc 的高级技术
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
【0】README
1)本文部分文字描述轉自:“Spring In Action(中/英文版)”,旨在review ?“spring(7)spring mvc 的高級技術”?的相關知識;
2)本文將會看到如何編寫控制器來處理文件上傳,如何處理控制器所拋出的異常,以及如何在模型中傳遞數據,使其能夠在重定向之后仍然存活;
【1】spring mvc 配置的替代方案 【1.1】 自定義 DispatcherServlet配置 【1.2】添加其他的Servlet 和 Filter 【1.3】在web.xml 中聲明 DispatcherServlet
【2】處理multipart 形式的數據 1)應用需求:Spittr 在新用戶注冊的時候需要上傳頭像,在發布Spittle的時候需要插入圖片(同微博一樣); 2)intro:圖片是二進制數據,multipart格式的數據 會將一個表單拆分為多個部分(part),每個部分對應一個輸入域; 3)在一般表單輸入域中,它所對應的部分會防止文本型數據,但如果上傳文件的話,它所對應的部分可以是二進制,下面展現了 multipart的請求體;
【2.1】配置multipart解析器 1)intro:DispatcherServlet并沒有實現任何解析 multipart 請求數據的功能,它將該任務委托給了 spring 中 MultipartResolver 接口的實現,通過這個實現類來解析multipart 請求中的內容; 2)spring3.1 開始,內置了兩個 MultipartResolver 的實現; 2.1)CommonsMultipartResolver:使用 Jakarta Commons ?FileUpload 解析 multipart請求; 2.2)StandardServletMultipartResolver:依賴于Servlet3.0 對?multipart 請求 的支持;(干貨——優選方案,因為它不依賴于第三方庫)
【2.1.1】使用 Servlet3.0解析?multipart請求 1)在spring 應用上下文中,將StandardServletMultipartResolver 聲明為bean : @Beanpublic MultipartResolver multipartResolver() throws IOException {return new StandardServletMultipartResolver();} 2)如何限制 StandardServletMultipartResolver 的工作方式呢?(如限制用戶上傳文件的大小和文件類型)(干貨——我們就不會直接創建 DispatcherServlet實例并將其注冊到 Servlet上下文中) 2.1)看個荔枝:最基本的 DispatcherServlet multipart配置,它將臨時路徑設置為 "/tmp/spittr/uploads" DispatcherServlet ds = new DispatcherServlet(); Dynamic registration = context.addServlet("appServlet", ds); registration.addMapping("/"); registration.setMultipartConfig(new MultipartConfigElement("/tmp/spittr/uploads")); 2.2)如果配置DispatcherServlet 的 Servlet初始化類繼承了 AbstractAnnotationConfigDispatcherServletInitializer 或 AbstractDispatcherServletInitializer,那么我們就不會直接創建 DispatcherServlet實例并將其注冊到 Servlet上下文中;這樣的話,將不會有對 Dynamic Servlet registration 的引用供我們使用了。但我們可以通過重載 customizeRegistration() 方法 來配置 multipart 的具體細節; @Override protected void customizeRegistration(Dynamic registration) {registration.setMultipartConfig(new MultipartConfigElement("/tmp/spittr/uploads")); } 對以上代碼的分析(Analysis):上述代碼所使用的 只有一個參數的?MultipartConfigElement 構造器,指定的是文件系統中的一個絕對目錄,上傳文件將會臨時寫入到該目錄中; 3)處理設置臨時路徑,還可以設置其他參數(parameters): parameter1)上傳文件的最大容量(以字節為單位),默認是沒有限制的; parameter2)整個mulitpart 請求的最大容量(以字節為單位),不會關心有多少個part以及每個part的大小,默認是沒有限制的; parameter3)在上傳的過程中,如果文件大小得到了一個指定最大容量(以字節為單位),將會寫入到臨時文件路徑中。默認值為0.也就是所上傳的文件都會寫入到磁盤上;
3.1)看個荔枝:限制文件大小不超過2M,整個請求不超過4M,而且所有的文件都寫到磁盤上,則設置為: @Override protected void customizeRegistration(Dynamic registration) {registration.setMultipartConfig(new MultipartConfigElement("/tmp/spittr/uploads",2097152, 4194304, 0)); } Supplement)使用xml 配置來設置的話,如下: <servlet><servlet-name>appServlet</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><load-on-startup>1</load-on-startup><multipart-config><location>/tmp/spittr/uploads</location><max-file-size>2097152</max-file-size> <max-request-size>4194304</max-request-size></multipart-config> </servlet> 【2.1.2】 配置 JAKARTA COMMONS FILEUPLOAD MULTIPART RESOLVER 1)intro:如果我們需要將應用部署到非 Servlet3.0 容器中,就使用該配置;spring 內置了CommonsMultipartResolver,可以作為 StandardServletMultipartResolver 配置的替代方案; 2)如何配置CommonsMultipartResolver 2.1)將CommonsMultipartResolver 聲明為 spring bean的 簡單方式如下: @Bean public MultipartResolver multipartResolver() {return new CommonsMultipartResolver(); }2.2)CommonsMultipartResolver:不會強制要求設置臨時文件路徑,默認case下,這個路徑就是 Servlet容器的臨時目錄;不過通過 updateTempDir屬性類設置不同 位置; @Bean public MultipartResolver multipartResolver() throws IOException {CommonsMultipartResolver multipartResolver =new CommonsMultipartResolver();multipartResolver.setUploadTempDir(new FileSystemResource("/tmp/spittr/uploads"));return multipartResolver; } 2.3)看個荔枝: 設置最大的文件容量為2M,最大的內存大小為0字節(所有的文件都會寫到磁盤中),與MultipartConfigElement 不同的是,我們無法設置 multipart 請求整體的最大容量; @Bean public MultipartResolver multipartResolver() throws IOException {CommonsMultipartResolver multipartResolver =new CommonsMultipartResolver();multipartResolver.setUploadTempDir(new FileSystemResource("/tmp/spittr/uploads"));multipartResolver.setMaxUploadSize(2097152);multipartResolver.setMaxInMemorySize(0);return multipartResolver; } 【2.2】處理multipart請求 1)intro:保存圖片到文件系統有兩種方式(method) method1)multipartFile; method2)part形式 2)編寫spring 控制器來接收上傳的文件: 最常見的方式就是在某個控制器方法上添加 @RequestPart注解; 2.1)修改前臺模板添加上傳圖片插件;(省略) 2.2)修改控制器方法 @RequestMapping(value="/register", method=POST)public String processRegistration(@RequestPart("profilePicture") byte[] profilePicture, // highlight line.@Valid Spitter spitter,Errors errors) { //... } 對以上代碼的分析(Analysis): A1)profilePicture屬性:將會給定一個 byte數組,這個數組中包含了請求中對應的part數據(通過@RequestPart類指定);如果用戶提交表單的時候沒有選擇文件,那么這個數組是空的(而不是null); A2)獲取到圖片數據后:processRegistration方法接下來就是將文件保存到某個位置了;(下面講如何處理文件的存儲)
【2.2.1】接收 MultipartFile 1)intro:spring提供了MultipartFile接口,它為處理multipart數據提供了內容豐富的對象;
對以上代碼的分析(Analysis): A1)Multipart提供了獲取上傳文件byte的方式,但是它所提供的功能并不僅限于此,還能獲得原始的文件名,大小以及內容類型; A2)它還提供了一個 InputStream,用來將文件數據以流的方式進行讀取; A3)MultipartFile 還提供了一個便利的 transferTo()方法,能夠幫助我們將上傳的文件寫入到文件系統中; profilePicture.transferTo(new File("/data/spittr/" + profilePicture.getOriginalFilename())); 【2.2.2】將文件保存到 Amazon S3中(省略)
【2.2.3】以Part的形式接收上傳的文件
1)intro:如果你需要將應用程序部署到 Servlet3.0的容器中,那么會有Multipart的一個替代方案——spring mvc 接收 javax.servlet.http.Part 作為控制器方法的參數; 2)若使用 Part來替換 MultipartFile的話,那么 processRegistration() 方法簽名會變成如下形式: @RequestMapping(value="/register", method=POST) public String processRegistration(@RequestPart("profilePicture") Part profilePicture,@Valid Spitter spitter,Errors errors) {//... } 3)Part接口方法一覽: package javax.servlet.http; import java.io.*; import java.util.*; public interface Part { public InputStream getInputStream() throws IOException;public String getContentType();public String getName();public String getSubmittedFileName();public long getSize();public void write(String fileName) throws IOException;public void delete() throws IOException;public String getHeader(String name);public Collection<String> getHeaders(String name);public Collection<String> getHeaderNames(); } 對以上代碼的分析(Analysis): A1)Part方法與 MultipartFile 方法有些類似:如getSubmittedFileName() 方法 同 getOriginalFilename()方法類似,write()方法 與 transferTo()方法類似; A2)借助于該方法(write方法),可以將上傳的文件寫入文件系統中: profilePicture.write("/data/spittr/" +profilePicture.getOriginalFilename()); Attention)只有使用 MultipartFile 的時候才需要 MultipartResolver;
【3】處理異常 1)intro:spring提供了多種方式將異常轉換為響應: way1)特定的spring 異常將會自動映射為指定 的 HTTP 狀態碼; way2)異常上可以添加 @ResponseStatus注解,從而將其映射為某一個HTTP 狀態碼; way3)在方法上添加 @ExceptionHandler 注解,使其用來處理異常; 2)處理異常的最簡單的方法:就是將其映射到 HTTP 狀態碼上,進而放到響應中;
【3.1】將異常映射為 HTTP 狀態碼 1)intro:spring 的一些異常會默認映射為 HTTP狀態碼;
對上表的分析(Analysis): 以上異常一般會由 spring 自身拋出,作為 DispatcherServlet處理過程中或執行校驗時出現問題的結果;
2)@ResponseStatus注解:spring 提供了一種機制,通過@ResponseStatus注解 將異常也會為 HTTP 狀態碼;
2.1)不加 @ResponseStatus注解的case:SpittleNotFoundException 將會產生500狀態碼的響應,實際上,如果出現任何沒有映射的異常,響應都會帶有500狀態碼; 2.2)加上 @ResponseStatus注解的case:使用該注解將 SpittleNotFoundException 映射為 HTTP 狀態碼 404; @ResponseStatus(value=HttpStatus.NOT_FOUND,reason="spittle not found") public class SpittleNotFoundException extends RuntimeException{ }
【3.2】編寫異常處理的方法 1)problem+solution: 1.1)problem:如果我們想在響應中不僅要包括狀態碼,還要包含所產生的錯誤,怎么來處理? 1.2)solution:我們不能將異常視為 HTTP 錯誤了,而是要按照處理請求的方式來處理異常; 2)看個荔枝:
對以上代碼的分析(Analysis):若視圖創建的Spittle 已存在數據庫中,則拋出DuplicateSpittleException,這樣一來,該方法就有兩條路徑,每個路徑有不同的輸出;
3)如何讓saveSpittle方法只關注正確的路徑,而讓其他方法處理異常? 3.1)step1:首先將saveSpittle方法中的異常處理剝離掉; 3.2)step2:為SpittleController 添加新方法,處理拋出的異常;
對@ExceptionHandler注解的分析:該注解能處理同一個控制器中所有處理器方法所拋出的異常;所以我們不用在每個可能拋出 DuplicateSpittleException 方法中添加異常處理代碼,這一個方法就涵蓋了所有功能;
【4】為控制器添加通知 1)intro:有沒有一種方法能夠處理所有控制器中處理器方法所拋出的異常呢? 2)spring3.2 提供了solution:控制器通知,它是任意帶有 @ControllerAdvice注解 的類,這個類會包含一個或多個如下類型的方法; func1)@ExceptionHandler 注解標注的方法; func2)@InitBinder注解標注的方法; func3)@ModelAttribute注解標注的方法; Attention)在帶有@ControllerAdvice注解的類中,以上這些方法會運用到整個應用程序所有控制器中帶有 @RequestMapping注解的方法上; 3)@ControllerAdvice注解最為實用的一個case是:將所有 @ExceptionHandler 方法收集到一個類中,這樣所有控制器的異常就能在一個地方進行一致的處理了;(干貨——@ControllerAdvice注解最為實用的一個case)
4)看個荔枝:如我們想將DuplicateSpittleException 的處理方法用到整個應用程序的所有控制器上;
【5】跨重定向請求傳遞數據 1)傳遞數據; 1.1)傳遞簡單數據(如String,int類型):使用 URL 模板進行重定向; 1.2)傳遞復雜數據(如對象):使用 Flash 屬性; 2)problem+solution: 2.1)problem:正在發起重定向功能的方法該如何發送數據給重定向的目標方法呢?一般來講,當一個處理器方法完成后,該方法所指定的模型數據將會copy 到 請求中,并作為請求中的屬性,請求會轉發(forward)到視圖上進行渲染。因為控制器方法和視圖所處理的是同一個請求,所以在轉發過程中,請求屬性能夠得以保存;但是重定向(redirect) 的case就不同了;
2.2)solution:如下圖所示,當控制器的結果是重新向的話,原始的請求就結束了,并且會發出一個新的 GET ?請求;原始請求中所帶有的模型數據也就消亡了;
Attention)顯然,對于重定向來說,模型并不能用來傳遞數據;(干貨——對于重定向來說,模型并不能用來傳遞數據)
3)其他數據傳遞方案:能夠從發起重定向的方法傳遞數據給處理重定向方法中: way1)使用 URL 模板以路徑變量 或/和 查詢參數的形式傳遞數據; way2)通過flash 屬性發送數據;
【5.1】 通過URL 模板進行重定向 1)通過路徑變量和查詢參數傳遞數據;(以下使用String連接的寫法很危險) return "redirect:/spitter/{username}"; 2)spring還提供了使用模板的方式來定義重定向URL: @RequestMapping(value="/register", method=RequestMethod.POST)public String processRegistration(Spitter spitter, Model model) {spitterRepository.save(spitter);model.addAttribute("username", spitter.getUsername());return "redirect:/spitter/{username}";} 對以上代碼的分析:username 作為占位符填充到了URL 模板中,所以username中所有不安全字符都會進行轉義; 3)除此之外,模型中所有其他的原始類型值都可以添加到 URL中作為查詢參數; @RequestMapping(value = "/register", method = RequestMethod.POST)public String processRegistration2(Spitter spitter, Model model) {spitterRepository.save(spitter);model.addAttribute("username", spitter.getUsername()); // highlight line.model.addAttribute("spitterId", spitter.getId()); // highlight line.return "redirect:/spitter/{username}";} 對以上代碼的分析: 因為模型中的spitterId 屬性沒有匹配重定向URL 中的任何占位符,所以它會自動以查詢參數的形式附加到重定向URL上;
Attention)通過路徑變量和查詢參數傳遞數據 有一個限制: 它只能用來發送簡單的數據(如String類型 和 數字的值);
【5.2】使用flash屬性(發送復雜數據) 1)problem+solution: 1.1)problem:發送實際的 Spitter對象,而不是簡單的int類型數據; 1.2)solution: 1.2.1)schema1:將Spitter對象 放入到 會話中,然后重定向后再將其從會話中取出; 1.2.2)schema2:spring 提供了提供了將數據發送為 flash 屬性的功能。flash 屬性會一直攜帶這些數據直到下一次請求才會消失;(干貨——flash屬性的作用) 2)RedirectAttributes 提供了一組 addFlashAttribute() 方法來添加flash屬性,如下:
3)通過flash屬性傳遞數據的原理:在重定向前,所有的flash屬性都會復制到會話中,重定向后,存在會話中的flash 屬性會被取出,并從會話轉移到模型中,如下圖所示:
4)看個荔枝:
【1】spring mvc 配置的替代方案 【1.1】 自定義 DispatcherServlet配置 【1.2】添加其他的Servlet 和 Filter 【1.3】在web.xml 中聲明 DispatcherServlet
【2】處理multipart 形式的數據 1)應用需求:Spittr 在新用戶注冊的時候需要上傳頭像,在發布Spittle的時候需要插入圖片(同微博一樣); 2)intro:圖片是二進制數據,multipart格式的數據 會將一個表單拆分為多個部分(part),每個部分對應一個輸入域; 3)在一般表單輸入域中,它所對應的部分會防止文本型數據,但如果上傳文件的話,它所對應的部分可以是二進制,下面展現了 multipart的請求體;
【2.1】配置multipart解析器 1)intro:DispatcherServlet并沒有實現任何解析 multipart 請求數據的功能,它將該任務委托給了 spring 中 MultipartResolver 接口的實現,通過這個實現類來解析multipart 請求中的內容; 2)spring3.1 開始,內置了兩個 MultipartResolver 的實現; 2.1)CommonsMultipartResolver:使用 Jakarta Commons ?FileUpload 解析 multipart請求; 2.2)StandardServletMultipartResolver:依賴于Servlet3.0 對?multipart 請求 的支持;(干貨——優選方案,因為它不依賴于第三方庫)
【2.1.1】使用 Servlet3.0解析?multipart請求 1)在spring 應用上下文中,將StandardServletMultipartResolver 聲明為bean : @Beanpublic MultipartResolver multipartResolver() throws IOException {return new StandardServletMultipartResolver();} 2)如何限制 StandardServletMultipartResolver 的工作方式呢?(如限制用戶上傳文件的大小和文件類型)(干貨——我們就不會直接創建 DispatcherServlet實例并將其注冊到 Servlet上下文中) 2.1)看個荔枝:最基本的 DispatcherServlet multipart配置,它將臨時路徑設置為 "/tmp/spittr/uploads" DispatcherServlet ds = new DispatcherServlet(); Dynamic registration = context.addServlet("appServlet", ds); registration.addMapping("/"); registration.setMultipartConfig(new MultipartConfigElement("/tmp/spittr/uploads")); 2.2)如果配置DispatcherServlet 的 Servlet初始化類繼承了 AbstractAnnotationConfigDispatcherServletInitializer 或 AbstractDispatcherServletInitializer,那么我們就不會直接創建 DispatcherServlet實例并將其注冊到 Servlet上下文中;這樣的話,將不會有對 Dynamic Servlet registration 的引用供我們使用了。但我們可以通過重載 customizeRegistration() 方法 來配置 multipart 的具體細節; @Override protected void customizeRegistration(Dynamic registration) {registration.setMultipartConfig(new MultipartConfigElement("/tmp/spittr/uploads")); } 對以上代碼的分析(Analysis):上述代碼所使用的 只有一個參數的?MultipartConfigElement 構造器,指定的是文件系統中的一個絕對目錄,上傳文件將會臨時寫入到該目錄中; 3)處理設置臨時路徑,還可以設置其他參數(parameters): parameter1)上傳文件的最大容量(以字節為單位),默認是沒有限制的; parameter2)整個mulitpart 請求的最大容量(以字節為單位),不會關心有多少個part以及每個part的大小,默認是沒有限制的; parameter3)在上傳的過程中,如果文件大小得到了一個指定最大容量(以字節為單位),將會寫入到臨時文件路徑中。默認值為0.也就是所上傳的文件都會寫入到磁盤上;
3.1)看個荔枝:限制文件大小不超過2M,整個請求不超過4M,而且所有的文件都寫到磁盤上,則設置為: @Override protected void customizeRegistration(Dynamic registration) {registration.setMultipartConfig(new MultipartConfigElement("/tmp/spittr/uploads",2097152, 4194304, 0)); } Supplement)使用xml 配置來設置的話,如下: <servlet><servlet-name>appServlet</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><load-on-startup>1</load-on-startup><multipart-config><location>/tmp/spittr/uploads</location><max-file-size>2097152</max-file-size> <max-request-size>4194304</max-request-size></multipart-config> </servlet> 【2.1.2】 配置 JAKARTA COMMONS FILEUPLOAD MULTIPART RESOLVER 1)intro:如果我們需要將應用部署到非 Servlet3.0 容器中,就使用該配置;spring 內置了CommonsMultipartResolver,可以作為 StandardServletMultipartResolver 配置的替代方案; 2)如何配置CommonsMultipartResolver 2.1)將CommonsMultipartResolver 聲明為 spring bean的 簡單方式如下: @Bean public MultipartResolver multipartResolver() {return new CommonsMultipartResolver(); }2.2)CommonsMultipartResolver:不會強制要求設置臨時文件路徑,默認case下,這個路徑就是 Servlet容器的臨時目錄;不過通過 updateTempDir屬性類設置不同 位置; @Bean public MultipartResolver multipartResolver() throws IOException {CommonsMultipartResolver multipartResolver =new CommonsMultipartResolver();multipartResolver.setUploadTempDir(new FileSystemResource("/tmp/spittr/uploads"));return multipartResolver; } 2.3)看個荔枝: 設置最大的文件容量為2M,最大的內存大小為0字節(所有的文件都會寫到磁盤中),與MultipartConfigElement 不同的是,我們無法設置 multipart 請求整體的最大容量; @Bean public MultipartResolver multipartResolver() throws IOException {CommonsMultipartResolver multipartResolver =new CommonsMultipartResolver();multipartResolver.setUploadTempDir(new FileSystemResource("/tmp/spittr/uploads"));multipartResolver.setMaxUploadSize(2097152);multipartResolver.setMaxInMemorySize(0);return multipartResolver; } 【2.2】處理multipart請求 1)intro:保存圖片到文件系統有兩種方式(method) method1)multipartFile; method2)part形式 2)編寫spring 控制器來接收上傳的文件: 最常見的方式就是在某個控制器方法上添加 @RequestPart注解; 2.1)修改前臺模板添加上傳圖片插件;(省略) 2.2)修改控制器方法 @RequestMapping(value="/register", method=POST)public String processRegistration(@RequestPart("profilePicture") byte[] profilePicture, // highlight line.@Valid Spitter spitter,Errors errors) { //... } 對以上代碼的分析(Analysis): A1)profilePicture屬性:將會給定一個 byte數組,這個數組中包含了請求中對應的part數據(通過@RequestPart類指定);如果用戶提交表單的時候沒有選擇文件,那么這個數組是空的(而不是null); A2)獲取到圖片數據后:processRegistration方法接下來就是將文件保存到某個位置了;(下面講如何處理文件的存儲)
【2.2.1】接收 MultipartFile 1)intro:spring提供了MultipartFile接口,它為處理multipart數據提供了內容豐富的對象;
對以上代碼的分析(Analysis): A1)Multipart提供了獲取上傳文件byte的方式,但是它所提供的功能并不僅限于此,還能獲得原始的文件名,大小以及內容類型; A2)它還提供了一個 InputStream,用來將文件數據以流的方式進行讀取; A3)MultipartFile 還提供了一個便利的 transferTo()方法,能夠幫助我們將上傳的文件寫入到文件系統中; profilePicture.transferTo(new File("/data/spittr/" + profilePicture.getOriginalFilename())); 【2.2.2】將文件保存到 Amazon S3中(省略)
【2.2.3】以Part的形式接收上傳的文件
1)intro:如果你需要將應用程序部署到 Servlet3.0的容器中,那么會有Multipart的一個替代方案——spring mvc 接收 javax.servlet.http.Part 作為控制器方法的參數; 2)若使用 Part來替換 MultipartFile的話,那么 processRegistration() 方法簽名會變成如下形式: @RequestMapping(value="/register", method=POST) public String processRegistration(@RequestPart("profilePicture") Part profilePicture,@Valid Spitter spitter,Errors errors) {//... } 3)Part接口方法一覽: package javax.servlet.http; import java.io.*; import java.util.*; public interface Part { public InputStream getInputStream() throws IOException;public String getContentType();public String getName();public String getSubmittedFileName();public long getSize();public void write(String fileName) throws IOException;public void delete() throws IOException;public String getHeader(String name);public Collection<String> getHeaders(String name);public Collection<String> getHeaderNames(); } 對以上代碼的分析(Analysis): A1)Part方法與 MultipartFile 方法有些類似:如getSubmittedFileName() 方法 同 getOriginalFilename()方法類似,write()方法 與 transferTo()方法類似; A2)借助于該方法(write方法),可以將上傳的文件寫入文件系統中: profilePicture.write("/data/spittr/" +profilePicture.getOriginalFilename()); Attention)只有使用 MultipartFile 的時候才需要 MultipartResolver;
【3】處理異常 1)intro:spring提供了多種方式將異常轉換為響應: way1)特定的spring 異常將會自動映射為指定 的 HTTP 狀態碼; way2)異常上可以添加 @ResponseStatus注解,從而將其映射為某一個HTTP 狀態碼; way3)在方法上添加 @ExceptionHandler 注解,使其用來處理異常; 2)處理異常的最簡單的方法:就是將其映射到 HTTP 狀態碼上,進而放到響應中;
【3.1】將異常映射為 HTTP 狀態碼 1)intro:spring 的一些異常會默認映射為 HTTP狀態碼;
對上表的分析(Analysis): 以上異常一般會由 spring 自身拋出,作為 DispatcherServlet處理過程中或執行校驗時出現問題的結果;
2)@ResponseStatus注解:spring 提供了一種機制,通過@ResponseStatus注解 將異常也會為 HTTP 狀態碼;
2.1)不加 @ResponseStatus注解的case:SpittleNotFoundException 將會產生500狀態碼的響應,實際上,如果出現任何沒有映射的異常,響應都會帶有500狀態碼; 2.2)加上 @ResponseStatus注解的case:使用該注解將 SpittleNotFoundException 映射為 HTTP 狀態碼 404; @ResponseStatus(value=HttpStatus.NOT_FOUND,reason="spittle not found") public class SpittleNotFoundException extends RuntimeException{ }
【3.2】編寫異常處理的方法 1)problem+solution: 1.1)problem:如果我們想在響應中不僅要包括狀態碼,還要包含所產生的錯誤,怎么來處理? 1.2)solution:我們不能將異常視為 HTTP 錯誤了,而是要按照處理請求的方式來處理異常; 2)看個荔枝:
對以上代碼的分析(Analysis):若視圖創建的Spittle 已存在數據庫中,則拋出DuplicateSpittleException,這樣一來,該方法就有兩條路徑,每個路徑有不同的輸出;
3)如何讓saveSpittle方法只關注正確的路徑,而讓其他方法處理異常? 3.1)step1:首先將saveSpittle方法中的異常處理剝離掉; 3.2)step2:為SpittleController 添加新方法,處理拋出的異常;
對@ExceptionHandler注解的分析:該注解能處理同一個控制器中所有處理器方法所拋出的異常;所以我們不用在每個可能拋出 DuplicateSpittleException 方法中添加異常處理代碼,這一個方法就涵蓋了所有功能;
【4】為控制器添加通知 1)intro:有沒有一種方法能夠處理所有控制器中處理器方法所拋出的異常呢? 2)spring3.2 提供了solution:控制器通知,它是任意帶有 @ControllerAdvice注解 的類,這個類會包含一個或多個如下類型的方法; func1)@ExceptionHandler 注解標注的方法; func2)@InitBinder注解標注的方法; func3)@ModelAttribute注解標注的方法; Attention)在帶有@ControllerAdvice注解的類中,以上這些方法會運用到整個應用程序所有控制器中帶有 @RequestMapping注解的方法上; 3)@ControllerAdvice注解最為實用的一個case是:將所有 @ExceptionHandler 方法收集到一個類中,這樣所有控制器的異常就能在一個地方進行一致的處理了;(干貨——@ControllerAdvice注解最為實用的一個case)
4)看個荔枝:如我們想將DuplicateSpittleException 的處理方法用到整個應用程序的所有控制器上;
【5】跨重定向請求傳遞數據 1)傳遞數據; 1.1)傳遞簡單數據(如String,int類型):使用 URL 模板進行重定向; 1.2)傳遞復雜數據(如對象):使用 Flash 屬性; 2)problem+solution: 2.1)problem:正在發起重定向功能的方法該如何發送數據給重定向的目標方法呢?一般來講,當一個處理器方法完成后,該方法所指定的模型數據將會copy 到 請求中,并作為請求中的屬性,請求會轉發(forward)到視圖上進行渲染。因為控制器方法和視圖所處理的是同一個請求,所以在轉發過程中,請求屬性能夠得以保存;但是重定向(redirect) 的case就不同了;
2.2)solution:如下圖所示,當控制器的結果是重新向的話,原始的請求就結束了,并且會發出一個新的 GET ?請求;原始請求中所帶有的模型數據也就消亡了;
Attention)顯然,對于重定向來說,模型并不能用來傳遞數據;(干貨——對于重定向來說,模型并不能用來傳遞數據)
3)其他數據傳遞方案:能夠從發起重定向的方法傳遞數據給處理重定向方法中: way1)使用 URL 模板以路徑變量 或/和 查詢參數的形式傳遞數據; way2)通過flash 屬性發送數據;
【5.1】 通過URL 模板進行重定向 1)通過路徑變量和查詢參數傳遞數據;(以下使用String連接的寫法很危險) return "redirect:/spitter/{username}"; 2)spring還提供了使用模板的方式來定義重定向URL: @RequestMapping(value="/register", method=RequestMethod.POST)public String processRegistration(Spitter spitter, Model model) {spitterRepository.save(spitter);model.addAttribute("username", spitter.getUsername());return "redirect:/spitter/{username}";} 對以上代碼的分析:username 作為占位符填充到了URL 模板中,所以username中所有不安全字符都會進行轉義; 3)除此之外,模型中所有其他的原始類型值都可以添加到 URL中作為查詢參數; @RequestMapping(value = "/register", method = RequestMethod.POST)public String processRegistration2(Spitter spitter, Model model) {spitterRepository.save(spitter);model.addAttribute("username", spitter.getUsername()); // highlight line.model.addAttribute("spitterId", spitter.getId()); // highlight line.return "redirect:/spitter/{username}";} 對以上代碼的分析: 因為模型中的spitterId 屬性沒有匹配重定向URL 中的任何占位符,所以它會自動以查詢參數的形式附加到重定向URL上;
Attention)通過路徑變量和查詢參數傳遞數據 有一個限制: 它只能用來發送簡單的數據(如String類型 和 數字的值);
【5.2】使用flash屬性(發送復雜數據) 1)problem+solution: 1.1)problem:發送實際的 Spitter對象,而不是簡單的int類型數據; 1.2)solution: 1.2.1)schema1:將Spitter對象 放入到 會話中,然后重定向后再將其從會話中取出; 1.2.2)schema2:spring 提供了提供了將數據發送為 flash 屬性的功能。flash 屬性會一直攜帶這些數據直到下一次請求才會消失;(干貨——flash屬性的作用) 2)RedirectAttributes 提供了一組 addFlashAttribute() 方法來添加flash屬性,如下:
3)通過flash屬性傳遞數據的原理:在重定向前,所有的flash屬性都會復制到會話中,重定向后,存在會話中的flash 屬性會被取出,并從會話轉移到模型中,如下圖所示:
4)看個荔枝:
總結
以上是生活随笔為你收集整理的spring(7)spring mvc 的高级技术的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: html 5怎么改变背景颜色(html5
- 下一篇: springmvc sends and