Spring學習筆記之Spring MVC的高階技術
本篇主要介紹利用Spring MVC處理檔案上傳,異常處理,為控制器新增通知以及跨重定向請求傳遞資料。
1.Spring MVC處理檔案上傳
1.1使用multipart格式提交表單
在有檔案上傳的表單中,我們需要使用multipart格式的資料來上傳,multipart格式的資料會將一個表單拆分成多個部分(part),每個部分對應一個輸入域。在一半的表單輸入域中,它所對應的部分會放置文字型資料,但是如果上傳檔案的話,它所對應的部分可以是二進位制。下面是前段頁面表單部分:
<form action="${ctx}/register" enctype="multipart/form-data" method="post">
<label>使用者名稱:</label>
<input type="text" name="username"><br/>
<label>密碼:</label>
<input type="password" name="password"><br/>
<label>郵箱:</label>
<input type="text" name="email" ><br/>
<label>頭像:</label>
<input type="file" name="image" accept="image/jpeg,image/png,image/gif"><br/>
<input type="submit" value="註冊">
</form>
form表單現在將enctype屬性設定為multipart/form-data,這會告訴瀏覽器以multipart資料的形式提交表單,而不是以表單資料的形式進行提交。在multipart中,每個輸入域都會對應一個part。而檔案上傳輸入域中的accept屬性用來將檔案型別限制為JPEG、PNG和GIF圖片。
1.2配置multipart解析器
DispatcherServlet將解析multipart請求資料的任務委託給了Spring中MultipartResolver策略介面的實現。Spring內建了兩個MultipartResolver的實現供我們選擇:
- CommonsMultipartResolver:使用Jakarta Commons FileUpload解析multipart請求
- StandardServletMultipartResolver:依賴於Servlet3.0對multipart請求的支援。
使用Servlet 3.0解析multipart請求:
在Spring MVC上下文中將其宣告為bean:
@Configuration
@EnableWebMvc //啟用Spring MVC
@ComponentScan("spittr.web") //啟用元件掃描
public class WebConfig
extends WebMvcConfigurerAdapter{
/**
* 配置multipart解析器
* @return
*/
@Bean
public MultipartResolver multipartResolver(){
return new StandardServletMultipartResolver();
}
}
StandardServletMultipartResolver的配置是在Servlet中通過傳入一個MultipartConfigElement例項來指定:
如果是自定義的Servlet,即(自己實現WebApplicationInitializer),這樣做:
public class myServletIntializer implements WebApplicationInitializer{
public void onStartup(ServletContext servletContext) throws ServletException {
MyServlet myServlet = new MyServlet();
Dynamic dynamic = servletContext.addServlet("myServlet", myServlet);
dynamic.addMapping("/");
dynamic.setMultipartConfig(
new MultipartConfigElement("/tmp/spittr/uploads"));
}
}
如果是通過繼承AbstractAnnotationConfigDispatcherServletInitializer的到的DispatcherServlet的話,將不會有對Dynamic的引用,還記得上一篇我們提到的麼,通過過載customizeRegistration會提供一個Dynamic引數,so:
@Override
protected void customizeRegistration(Dynamic registration) {
registration.setMultipartConfig(
new MultipartConfigElement("/tmp/spittr/uploads"));
}
這裡,我們都是通過構造器來對MultipartConfigElement進行配置的。new MultipartConfigElement(location, maxFileSize, maxRequestSize, fileSizeThreshold)
這是其所有的構造器。
- location:用來指定上傳檔案的臨時寫入目錄,一定要注意是絕對目錄,比如Windows下的F:\Workspace\spring,上面我們配置的是Linux下的/tmp/spittr/uploads
- maxFileSize:上傳檔案的最大容量(以位元組為單位),預設無限制
- maxRequestSize:整個multipart請求的最大容量(以位元組為單位),不會關心有多少個part以及每個part的大小。預設是沒有限制的
- fileSizeThreshold:在檔案上傳的過程中,如果檔案大小達到了一個指定最大容量(以位元組為單位),將會寫入到臨時檔案路徑中。預設值為0,也就是所有上床的檔案都會寫入到磁碟上。
下面是用web.xml來配置MultipartConfigElement,使用<servlet>
的<multipart-config>
元素:
<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>
<file-size-threshold>0</file-size-threshold>
</multipart-config>
</servlet>
限制檔案的大小為2MB,整個請求不超過4MB,而且所有的檔案都寫到磁盤裡。
使用使用Jakarta Commons FileUpload解析multipart請求
宣告為bean:
@Bean
public MultipartResolver multipartResolver(){
//return new StandardServletMultipartResolver();
return new CommonsMultipartResolver();
}
CommonsMultipartResolver的配置不是在Servlet指定的,而是直接配置在例項中,而且CommonsMultipartResolver不會輕質要求設定臨時檔案路徑,預設情況下,這個路徑就是Servlet容器的臨時目錄。下面是配置一個等價於上面我們對MultipartConfigElement的配置:
@Bean
public MultipartResolver multipartResolver() throws IOException{
//return new StandardServletMultipartResolver();
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
multipartResolver.setUploadTempDir(
new FileSystemResource("/tmp/spittr/uploads"));//對應於MultipartConfigElement的location
multipartResolver.setMaxUploadSize(2097152);//對應於MultipartConfigElement的maxFileSize
multipartResolver.setMaxInMemorySize(0);//對應於MultipartConfigElement的fileSizeThreshold
return multipartResolver;
}
CommonsMultipartResolver 無法設定multipart請求整體的最大容量。
1.3處理multipart請求
1.3.1用byte[]陣列來接受上傳檔案的二進位制資料
程式碼:
@RequestMapping("/register")
public void spittle(
@RequestPart("image") byte[] image,
User user){
System.out.println(image.length);
}
1.3.2接受MultipartFile:
@RequestMapping("/register")
public void spittle(
@RequestPart("image") MultipartFile image,
User user,HttpServletRequest request) throws IllegalStateException, IOException{
System.out.println(image.getContentType());
System.out.println(image.getName());
System.out.println(image.getOriginalFilename());
System.out.println(image.getSize());
image.transferTo(
new File("/uploads/"+image.getOriginalFilename()));//將上傳檔案寫入到檔案系統中
}
需要注意的是這裡File的位置是以你配置的臨時檔案路徑為前提的,比如說你的臨時檔案路徑為F:\Workspace\spring,那麼上面的檔案就是在F:\Workspace\spring\uploads\下
2.處理異常
Servlet請求的輸出都是一個Servlet響應。如果在請求處理的時候,出現了異常,那它放入輸出依然會使Servlet響應。異常必須要以某種方法轉換為響應。
Spring提供了多種方式將異常轉換為響應:
- 特定的Spring異常將會自動對映為指定的HTTP狀態碼
- 異常上可以新增@ResponseStatus註解,從而將其對映為某一個HTTP狀態碼
- 在方法上可以新增@ExceptionHandler註解,使其用來處理異常。
2.1將異常對映為HTTP狀態碼
Spring的一些異常會預設對映為HTTP狀態碼
Spring異常 | HTTP狀態碼 |
---|---|
BindException | 400-Bad Request |
ConversionNotSupportedException | 500-Internal Server Error |
HttpMediaTypeNotAcceptableException | 406-Not Acceptable |
HttpMediaTypeNotSupportedException | 415-Unsupported Media Type |
HttpMessageNotReadableException | 400-Bad Request |
HttpMessageNotWritableException | 500-Internal Server Error |
HttpRequestMethodNotSupportedException | 405-Method Not Allowed |
MethodArgumentNotValidException | 400-Bad Request |
MissingServletRequestParameterException | 400-Bad Request |
MissingServletRequestPartException | 400-Bad Request |
NoSuchRequestHandlingMethodException | 404-Not Found |
TypeMismatchException | 400-Bad Request |
下面介紹將異常對映為特定的狀態碼
首先我們建立一個異常,在其上面使用@ResponseStatus來將異常對映為特定的狀態碼。
@ResponseStatus(value=HttpStatus.NOT_FOUND,
reason="Param Not Found")//將異常對映為HTTP狀態404
public class ParamNotFountException extends RuntimeException{
private static final long serialVersionUID = 1L;
}
在控制器如果接受到的username為空則丟擲這個異常:
@RequestMapping("/register")
public String spittle(
@RequestPart("image") MultipartFile image,
User user,HttpServletRequest request){
//如果使用者名稱為空則丟擲異常
if(user.getUsername().isEmpty()){
throw new ParamNotFountException();
}
return "register";
}
截圖:
2.2編寫異常處理的方法
繼續上面的方法,如果我們不想顯示錯誤頁面,想要捕獲異常的話,平時我們會使用catch進行捕獲,但catch只能捕獲當前語句塊的異常並處理。下面我們來使用@ExceptionHandler標註的方法,它可以出來同一個控制器中所有處理器方法丟擲的異常。接著上面的程式碼,我們在控制器方法下面寫這樣一個方法:
@RequestMapping("/register")
public String spittle(
@RequestPart("image") MultipartFile image,
User user,HttpServletRequest request){
//如果使用者名稱為空則丟擲異常
if(user.getUsername().isEmpty()){
throw new ParamNotFountException();
}
return "register";
}
//捕獲這個控制器丟擲的ParamNotFountException異常,並處理
@ExceptionHandler(value=ParamNotFountException.class)
public String HandleException(){
System.out.println("在這裡處理異常");
return "error";
}
這一次,返回的不是404頁面,而是error頁面,而且控制檯輸出:“在這裡處理異常”欄位。
3.為控制器新增通知
控制器通知(controller advice)是任意帶有@ControllerAdvice註解的類,這個類會包含一個或多個如下型別的方法:
- @ExceptionHandler註解標註的方法;
- @InitBinder註解標註的方法;
- @ModelAttribute註解標註的方法。
在帶有@ControllerAdvice註解的類中,以上所述的這些方法會運用到整個應用程式所有控制器中帶有@RequestMapping註解的方法上。@ControllerAdvice註解本身自帶掃描特性,即使用了@Component。
還是上面的程式碼,我們把控制器中的HandleException()方法刪掉,然後新建一個類,執行效果是一樣的:
@ControllerAdvice
public class AppWideException {
//捕獲所有控制器丟擲的ParamNotFountException異常,並處理
@ExceptionHandler(value=ParamNotFountException.class)
public String HandleException(){
System.out.println("在這裡處理異常");
return "error";
}
}
4.跨重定向請求傳遞資料
當一個處理器方法完成之後,該方法所指定的模型資料將會複製到請求中,並作為請求的屬性,請求會轉發(forward)到檢視上進行渲染。因為控制器方法和檢視所處理的是同一個請求,所以在轉發的過程中,請求屬效能夠得以儲存。當控制器的結果是重定向的話,原始的請求就結束了,並且會發起一個新的get請求。原始請求中所帶有的模型資料也就隨之請求一起消亡了。
有兩種方法能夠從發起重定向的方法傳遞資料給處理重定向方法中:
- 使用URL模板以路徑變數和/或查詢引數的形式傳遞資料
- 通過flash屬性發生資料
4.1通過URL模板進行重定向
使用URL中的佔位符進行傳遞:
@RequestMapping("/register")
public String spittle(User user,Model model){
model.addAttribute("username", "xuexiaoqiang");
model.addAttribute("email", "[email protected]");
//重定向
return "redirect:/test/{username}";
}
@RequestMapping("/test/{username}")
public String test(
@PathVariable(value="username") String username,
HttpServletRequest request){
String email = request.getParameter("email");
System.out.println("username:"+username);
System.out.println("email:"+email);
return "register";
}
輸出:
username:xuexiaoqiang
email:test@tom.com
這裡,因為模型中的email沒有匹配重定向URL中的任何佔位符,所以它會自動以查詢引數的形式附加到重定向URL上。這裡最後的重定向路徑是“/test/[email protected]”。
4.2使用flash屬性
如果我們想要發生一個物件,那麼URL就不能實現,Spring提供了將資料發生為flash屬性的功能。按照定義,flash屬性會一直攜帶這些資料知道下一次請求,然後才會消失。
RedirectAttributes是Model的一個子介面,提供了Model的所有功能,除此之外,還有幾個方法是用來設定flash屬性的。
@RequestMapping("/register")
public String spittle(User user,RedirectAttributes model){
user.setEmail("[email protected]");
user.setUsername("xuexiaoqiang");
model.addFlashAttribute("user", user);
//重定向
return "redirect:/test";
}
@RequestMapping("/test")
public String test(Model model){
if(model.containsAttribute("user")){
System.out.println("存在");
}
return "testdata";
}
testdata.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>使用者名稱:${user.username }</h1>
<h1>郵箱:${user.email }</h1>
</body>
</html>
最後結果截圖:
在重定向執行之前,所有flash屬性都會複製到會話中。在重定向後,存在會話中的flash屬性會被取出,並從會話轉移到模型之中。處理重定向的方法就能從模型中訪問User物件了,就像獲取其他的模型物件一樣。
相關推薦
Spring學習筆記之Spring MVC的高階技術
本篇主要介紹利用Spring MVC處理檔案上傳,異常處理,為控制器新增通知以及跨重定向請求傳遞資料。 1.Spring MVC處理檔案上傳 1.1使用multipart格式提交表單 在有檔案上傳的表單中,我們需要使用multipart格式的資料來
Spring學習筆記之BeanDefinition
spring bean definition 在Spring容器中,Bean的實例以BeanDefinition來表示的。一個BeanDefinition描述了一個Bean實例。本文出自 “十裏稻花香” 博客,請務必保留此出處http://5880861.blog.51cto.com/587086
Spring學習筆記之BeanFactory
spring bean factory BeanFactory是一個頂級接口,下面看下它是幹什麽的。這裏說了,它是訪問Spring Bean容器的根接口,是Bean容器的基本視圖。它的一些子接口,比如ListableBeanFactory和ConfigurableBeanFactory都有特別的而
Spring學習筆記之ApplicationContext
spring application context 對於一個Application提供配置的核心接口,在應用運行的時候它是只讀的。一個ApplicationContext提供以下能力:1、可以通過BeanFactory中的方法訪問應用組件2、可以加載文件資源3、可以給已經註冊的監聽器發送事件4、
Spring學習筆記之啟動
spring 原理 啟動分析今天,以ClassPathXmlApplicationContext為例來看一下,Spring啟動的時候都做了什麽重點看refresh()方法refresh()方法是在AbstractApplicationContext類中定義的ClassPathXmlApplicationCo
Sprng Cloud學習筆記之Spring Cloud簡介
Spring Cloud Spring Cloud是一系列框架的有序集合(Spring Cloud並不是一個專案,它是一套專案的組合)。它利用Spring Boot的開發便利性巧妙地簡化了分散式系統基礎設施的開發,如服務發現註冊、配置中心、訊息匯流排、負載均衡、斷路器、資料監控等,都可以
1.Spring學習筆記之 ———— Bean的例項化
什麼是Bean的例項化? 在面向物件的程式中,想要使用某個物件,就需要先例項化這個物件。Spring中,想要使用容器中的Bean,也需要例項化Bean。 其類似於當我們需要建立一個類物件而去new這個類一樣。通常來說,當我們需要用到一個Bean的時
2.Spring學習筆記之 ————IoC(控制反轉)
控制反轉(IoC),是Spring裡一個專有的名詞,其意思就是說,物件的例項由Spring容器來進行建立而不是我們自己手動建立,當我們在Spring容器中設定好Bean屬性後,Spring容器就會自動建立其例項,我們只要去呼叫Spring的Bean就行。 接下來是例子:
Spring學習筆記之自動化裝配Bean
在Spring中可以使用Java程式碼、XML和自動化裝配三種方式來裝配Bean。從便利性角度來說,最強大的還是Spring的自動化配置,如果Spring能夠進行自動化裝配的話,那何苦還要顯式的將這些Bean裝配在一起呢? Spring從兩個角度來實現自動化裝配: 元件
Spring學習筆記之前置通知、後置通知
在學習之前先提一個概念。 AsprctJ:Java社群裡最完整最流行的AOP框架。在Spring2.0以上版本中,可以使用基於AspectJ註解或基於XML配置的AOP。 啟用AspectJ註解支援 要在 Spring 中宣告 AspectJ 切面,
Spring學習筆記之Bean的作用域
在預設情況下,Spring的應用上下文中所有的bean都是單例的形式建立的。也就是說,不管給定的一個bean被注入到其它bean多少次,每次注入的都是同一個例項。 在大多數情況下,單例bean是非常理想的方案。初始化和垃圾回收物件例項所帶來的成本只留給一些小規模任務,在這些任
spring學習筆記:ioc容器高階特性
Spring容器的高階特性涉及到屬性編輯器,使用外部屬性檔案,國際化,容器事件等等; 今天講解一下屬性編輯器,使用外部資源,國際化。 屬性編輯器 如果你沒有了解過屬性編輯器,建議你先google一下,我簡單的解釋一下什麼是屬性編輯器,看一個新寫的有代表性的bean:pack
《Pro Spring》學習筆記之Spring+ActiveMQ實現Queue通訊(點對點)
spring配置檔案: <?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="h
spring學習筆記:spring mvc不得不說的幾處配置
通常情況下會有這麼幾個配置檔案:web.xml, applicationContext.xml, xxxx-servlet.xml, 在web.xml中通常會這麼配置DispatcherServlet:<servlet> <servlet-name>
Spring學習筆記之--SpEL
本文內容來源於尚矽谷視訊教程,僅作為本文學習筆記使用,請勿轉載! 1.什麼是SpEL? Spring表示式語言,簡稱SpEL,是一個支援執行時查詢和操作物件的強大的表示式語言。語法類似於EL,格式為#{...}。SpEL為Bean的屬性進行動態賦值提供了便利。 2.可以做什
新手學習筆記之Spring編寫AOP半自動代理
invoke proc pro aspect 新手 接口 src info nbsp 1.導包: 2.目標類 package oyb.service; public interface UserService { public void add(
spring學習筆記(一) Spring概述
數據庫 spring容器 oot 基礎知識 spa 遠程 組合 主動 拓展 博主Spring學習筆記整理大部分內容來自Spring實戰(第四版)這本書. 強烈建議新手購入或者需要電子書的留言. 在學習Spring之前,我們要了解這麽幾個問題:什麽是Spring?Sprin
學習筆記之《Java核心技術卷I》---- 第八章 泛型程式設計
泛型類的定義格式:class Pair<T>{ } 普通類中泛型方法的定義:public static <T> T getMiddle(T... a){ return a[a.length / 2]; } 呼叫方法時,可以使用:ClassName.getMi
學習筆記之《Java核心技術卷I》---- 第七章 異常、斷言和日誌
異常物件都是派生與Throwable的一個例項 派生於Error類或RuntimeException類的所有異常稱為非受查異常,所有其他異常稱為受查異常 一個方法必須宣告所有可能丟擲的受查異常,而非受查異常要麼不可控制,要麼就應該避免發生 關鍵字throws位於方法之
學習筆記之《Java核心技術卷I》---- 第六章 介面、lambda表示式與內部類
介面中的所有方法都自動地屬於public。因此,在介面中宣告方法時,不必提供關鍵字public;但是在實現介面的類中,必須在實現介面中的方法時把介面中的方法宣告為public,如果不宣告,那就預設包訪問許可權,編譯器會報錯 實現Comparabale介面,必須實現其中的compareTo