1. 程式人生 > >**SpringMVC 筆記二 [共三篇,史上最全重點]*

**SpringMVC 筆記二 [共三篇,史上最全重點]*

今日內容:

1. 響應
2. 檔案上傳
3. 異常處理
4. 攔截器[作用不大了,瞭解]

響應

1. 概述:就是把後臺的資料返回給瀏覽器/使用者;
2. 搭建環境:
	1. 建立專案:Maven->選擇骨架或者手動新增..		在main中建立java,resources,webapp三個包,webapp包下建立WEB-INF,pages:建立succes.jsp;[成功的返回頁面]
	2. 匯入相關座標..	[可以應用版本鎖定]
	3. 引入前端控制器[固定寫法] 
	4. springmvc.xml:開啟註解掃描,檢視解析器,註解支援
	5. 刪除webapp中的index.jsp重新建立一個;
3. 響應資料和結果檢視:
	1. Controller方法返回字串   【使用Model存入資料再返回】
		1. 解析: Controller方法執行後,返回的字串根據檢視解析器所前後拼接的檔名和地址,就能成功的跳轉到所需的頁面;
		2. jsp頁面接收物件:
		* 在Controller方法中,使用model.addAttribute("user",user)			[該方法需傳入一個Model]
		* Model不需要我們建立,這是框架為我們生成的;
		* isELIgnored="false" [識別el表示式]
		* 使用${user.username}進行取值...
		* 示例:
		* @Controller
		* @RequestMapping("/user")
		* public class UserController {
		* /**
		* *請求引數的繫結
		* */
		* @RequestMapping(value="/initUpdate")
		* public String initUpdate(Model model) {
		* // 模擬從資料庫中查詢的資料
		* User user = new User();
		* user.setUsername("張三");
		* user.setPassword("123");
		* user.setMoney(100d);
		* user.setBirthday(new Date());
		* model.addAttribute("user", user);
		* return "update";
		* }
		* }
	2. Controller方法返回void 		【return;】
		1. 請求轉發:請求轉發是一次請求,不能簡寫	[手動跳動轉發,不會觸動檢視解析器,需要自己寫全路徑和檔案格式]  
			* 示例:request.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(request,response);
		2. 重定向:請求轉發是可以直接訪問web-inf下的檔案,所以不用寫虛擬地址;
			* reponse.sendRedirect(request.getContextPath()+"WEB-INF/pages/xxx.jsp")
		3. 解決中文亂碼:
			* reponse.setCharacterEncoding("UTF-8");
			* response.setContentType("text/html;charset=UTF-8");
		4. 直接響應:
			* response.getWriter().print("你好");
		5. 注意:程式執行到響應,那麼響應後面的程式碼應寫上return;
			* 一次請求對應一次響應,所以response.getWriter.print("xxx"),裡面列印的需要在整個方法執行完才會整體的去響應,在此之前它是將列印的語句儲備起來,還未傳送響應;所以使用return;進行結尾,那麼就可以不讓框架繼續執行其他,直接返回;
		6. 使用框架後,request,response這些以後都不經常用了;
		7. 為什麼domain實體類要實現Serializable介面?
			* 它是實體類,專門用來儲存類,它存在於記憶體中,實現序列化介面,可以將封裝好的類儲存到硬碟中,如果突然斷電關機什麼意外情況,可以反序列化還原;
			* 實現該介面,才可以將該物件遠端呼叫,遠端傳輸到其他的伺服器,傳輸時以流的形式
		8. 實體類中的常見問題:
			1. 如果實體類中寫了有參構造,那麼需要再為它建立一個無參構造;
			2. 成員變數最好使用private修飾,加強安全性,只能通過get,set對其操作;
			3. 成員變數儘量使用包裝型別,不要使用基本型別,即儘量使用Integer類,而不使用int基本型別; 
			4. Integer修飾的變數可以為空,但基本型別不能為null;	比如:private Integer ge=null;
	3. ModelAndView:
		1. 把user物件儲存到mv物件中,也會把user物件存入到request物件;
		2. 它既可以儲存鍵值對[發揮model],它也可以轉發檢視[view]的作用;
			* 示例:
			* @RequestMapping("/testModelAndView")
			* public ModelAndView testModelAndView(){
			* ModelAndView mv =new ModelAndView();
			* System.out.println("testModelAndView方法執行了...")
			* User user=new User();
			* user.setUsername("小楓")
			* user.setPassword(123);
			* user.setAge(30);
			* mv.addObject("user",user);
			* mv.setViewName("success");
			* reutrn mv;
			* }
	4. 使用關鍵字的方式進行轉發或者重定向		[使用關鍵字是不會被檢視解析器所管理,需要自己手動補全路徑]
		* @RequestMapping("/testForwardOrRedirect")
		* public String testForwardOrRedirect(){
		* //請求的轉發:
		* return "forward:/WEB-INF/pages/success.jsp";
		* //重定向
		* return "redirect:/index.jsp";
		* }
4. ResponseBody響應json資料
	1.  DispatcherServlet會攔截到所有的資源,導致一個問題就是靜態資源(img、css、js)也會被攔截到,從而不能被使用。解決問題就是需要配置靜態資源不進行攔截,在springmvc.xml配置檔案新增如下配置:
		* //前端控制前,哪些資源不攔截:
		* <mvc:resources location="/css/" mapping="/css/**"/>
		* <mvc:resources location="/images/" mapping="/images/**"/>
		* <mvc:resources location="/js/" mapping="/js/**"/>
	2. 特點:
		1. mvc:resources標籤配置不過濾
		2. location元素表示webapp目錄下的包下的所有檔案
		3. mapping元素表示以/static開頭的所有請求路徑,如/static/a 或者/static/a/b

5. 模擬示例:
	1. 模擬非同步請求響應:		
		1. 伺服器:
			* @RequestMapping("/testAjax")
			* public void testAjax(@RequestBody String body){
			* //
			* System.out.println(body);
			* }
		2. 瀏覽器:
			* $(function(){
			* $("#btn").click(funcation(){
			* $.ajax({
			* url:""user/testAjax",
			* contextType:"application/json;charset=UTF-8",				//設定了這種格式,提交是以json格式提交,如果不以此設定,$.get |$.post 則都是表單提交格式
			* data:'{"username":"heihei","password":"123","age":30}',
			* dataType:"json",
			* type:"post",
			* success:function(date){
			* //data伺服器端響應的json資料,進行解析。
			* }
			* })
			* })
		3. 如果傳入已經定義好了的domain裡User實體類,那麼也可以直接傳入這個實體類,只要屬性值對應的上,那麼springmvc就會自動繫結,與ajax傳送的屬性值一一對應;

檔案上傳

1. 檔案上傳三要素:    [只要是檔案上傳,就必須滿足這三個要素]
	1. form表單的enctype取值必須是:multipart/form-data
		* [預設值是:application/x-222-form-urlencoded]
		* enctype:是表單請求正文的型別
	2. method屬性必須是post		
		* get請求會把檔案攜帶在位址列上,而get請求是有大小限制的;
	3. 提供一個檔案選擇域而且必須有name屬性:
		* <input type="file">  
2. 藉助第三方元件實現檔案上傳:
	* 匯入commons-fileupload-1.3.1.jar和commons-io-2.4.jar
3. 案例實現:
	1. 搭建開發環境:
			1. 建立一個新的maven專案:
				* 座標,springmvc.xml,web.xml
				* <dependency>
					<groupId>commons-fileupload</groupId>
					<artifactId>commons-fileupload</artifactId>
					<version>1.3.1</version>
					</dependency>
					<dependency>
					<groupId>commons-io</groupId>
					<artifactId>commons-io</artifactId>
					<version>2.4</version>
					</dependency>
			2. 補全包路徑
			3. 編寫檔案上傳的JSP頁面:
				<h3>檔案上傳</h3>
				<form action="user/fileupload" method="post" enctype="multipart/form-data">
				選擇檔案:<input type="file" name="upload"/><br/>
				<input type="submit" value="上傳檔案"/>
				</form>
			4. 編寫檔案上傳的Controller控制器:
				* @RequestMapping(value="/fileupload")
					public String fileupload(HttpServletRequest request) throws Exception {
					// 先獲取到要上傳的檔案目錄
					String path = request.getSession().getServletContext().getRealPath("/uploads");
					// 建立File物件,一會向該路徑下上傳檔案
					File file = new File(path);
					// 判斷路徑是否存在,如果不存在,建立該路徑
					if(!file.exists()) {
					file.mkdirs();
					}
					// 建立磁碟檔案項工廠
					DiskFileItemFactory factory = new DiskFileItemFactory();
					ServletFileUpload fileUpload = new ServletFileUpload(factory);
					// 解析request物件
					List<FileItem> list = fileUpload.parseRequest(request);
					// 遍歷
					for (FileItem fileItem : list) {
					// 判斷檔案項是普通欄位,還是上傳的檔案
					if(fileItem.isFormField()) {
					}else {
					// 上傳檔案項
					// 獲取到上傳檔案的名稱
					String filename = fileItem.getName();
					// 上傳檔案
					fileItem.write(new File(file, filename));
					// 刪除臨時檔案
					fileItem.delete();
					}
					}
					return "success";
					}
5. SpringMVC實現原理分析:
	1. 選擇檔案後點擊上傳;
	2. 當檔案上傳後,通過request進入到前端控制器
	3. 前端控制器通過新增的配置檔案解析器,解析request並返回upload
	4. 前端控制器呼叫Controller,將upload傳入Controller類下的fileuoload2(MultipartFile upload),然後對其進行操作;
	5. 注意:前端的name屬性值必須和後端Controller類下finduoload2內傳入的引數名保持一致;
6. 實現程式碼:
	1. SpringMVC傳統方式檔案上傳:
		1. SpringMVC框架提供了MultipartFile物件,該物件表示上傳的檔案,要求變數名必須和表單file標籤的name屬性相同;
		2. 程式碼如下:
			@RequestMapping(value="/fileupload2")
			public String fileupload2(HttpServletRequest request,MultipartFile upload) throws
			Exception {
			System.out.println("SpringMVC方式的檔案上傳...");
			// 先獲取到要上傳的檔案目錄
			String path = request.getSession().getServletContext().getRealPath("/uploads");
			// 建立File物件,一會向該路徑下上傳檔案
			File file = new File(path);
			// 判斷路徑是否存在,如果不存在,建立該路徑
			if(!file.exists()) {
			file.mkdirs();
			}
			// 獲取到上傳檔案的名稱
			String filename = upload.getOriginalFilename();
			String uuid = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
			// 把檔案的名稱唯一化
			filename = uuid+"_"+filename;
			// 上傳檔案
			upload.transferTo(new File(file,filename));
			return "success";
			}
		3. 配置檔案解析器物件:		【如果是傳統的檔案上傳那麼會報錯,只有SpringMVC的檔案上傳才能起作用】
			<!-- 配置檔案解析器物件,要求id名稱必須是multipartResolver -->
			<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
			<property name="maxUploadSize" value="10485760"/>             // 10485760=1024*1024*10=10MB
			</bean>
			

	2. SpringMVC跨伺服器檔案上傳:
		1. 搭建圖片伺服器:
			1. 根據文件配置tomcat9的伺服器,現在是兩個伺服器
			2. 匯入資料中day02_SpringMVC5_02image專案,作為圖片伺服器使用		[兩個tomcat模擬兩臺伺服器,一臺伺服器作為控制器接收請求,一臺伺服器作為圖片儲存]
		2. 實現SpringMVC跨伺服器檔案上傳
1. 匯入開發需要的jar包:
					    <dependency>
						<groupId>com.sun.jersey</groupId>
						<artifactId>jersey-core</artifactId>
						<version>1.18.1</version>
						</dependency>
						<dependency>
						<groupId>com.sun.jersey</groupId>
						<artifactId>jersey-client</artifactId>
						<version>1.18.1</version>
						</dependency>
				2. 編寫檔案上傳JSP頁面:
						<h3>跨伺服器的檔案上傳</h3>
						<form action="user/fileupload3" method="post" enctype="multipart/form-data">
						選擇檔案:<input type="file" name="upload"/><br/>
						<input type="submit" value="上傳檔案"/>
						</form>
				3. 編寫控制器:
						@RequestMapping(value="/fileupload3")
						public String fileupload3(MultipartFile upload) throws Exception {
						System.out.println("SpringMVC跨伺服器方式的檔案上傳...");
						// 定義圖片伺服器的請求路徑
						String path = "http://localhost:9090/day02_springmvc5_02image/uploads/";
						// 獲取到上傳檔案的名稱
						String filename = upload.getOriginalFilename();
						String uuid = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
						// 把檔案的名稱唯一化
						filename = uuid+"_"+filename;
						// 向圖片伺服器上傳檔案
						// 建立客戶端物件
						Client client = Client.create();
						// 連線圖片伺服器
						WebResource webResource = client.resource(path+filename);
						// 上傳檔案
						webResource.put(upload.getBytes());
						return "success";
						}

異常處理

1. SpringMVC異常處理流程:
	1. 瀏覽器-> 前端控制器-> web-> service-> dao
	2. 如果出現錯誤,預設處理方案是將錯誤依次往上丟擲:
		* 假如dao層出現錯誤,會拋給service->web->前端控制器->瀏覽器
	3. 出現錯誤如果將錯誤出現在瀏覽器上,會降低使用者體驗,產生不好的影響;
		* 此時可在前端控制器新增一個異常處理器元件,它將從內部拋給前端控制器的異常攔截,並將異常進行處理,返回給瀏覽器一個友好的錯誤提示頁面;
	4. Controller呼叫service,service呼叫dao,異常都是向上丟擲的,最終有DispathcherServlet找異常處理器進行異常的處理。
	5. 示例:
1. 自定義異常類:
				package cn.itcast.exception;
				public class SysException extends Exception{
				private static final long serialVersionUID = 4055945147128016300L;
				// 異常提示資訊
				private String message;
				public String getMessage() {
				return message;
				}
				public void setMessage(String message) {
				this.message = message;
				}
				public SysException(String message) {
				this.message = message;
				}}
			2. 自定義異常處理器
				package cn.itcast.exception;
				import javax.servlet.http.HttpServletRequest;
				import javax.servlet.http.HttpServletResponse;
				import org.springframework.web.servlet.HandlerExceptionResolver;
				import org.springframework.web.servlet.ModelAndView;
				/**
				* 異常處理器
				* @author rt
				*/
				public class SysExceptionResolver implements HandlerExceptionResolver{
				/**
				* 跳轉到具體的錯誤頁面的方法
				*/
				public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse
				response, Object handler,
				Exception ex) {
				ex.printStackTrace();
				SysException e = null;
				// 獲取到異常物件
				if(ex instanceof SysException) {
				e = (SysException) ex;
				}else {
				e = new SysException("請聯絡管理員");
				}
				ModelAndView mv = new ModelAndView();
				// 存入錯誤的提示資訊
				mv.addObject("message", e.getMessage());
				// 跳轉的Jsp頁面
				mv.setViewName("error");
				return mv;
				}
				}
			3. 配置異常處理器:
				< bean id="sysExceptionResolver" class="cn.itcast.exception.SysExceptionResolvaer"/>

攔截器

1. SpringMVC的處理器攔截器類似於Servlet開發中的過濾器Filter,用於對處理器進行預處理和後處理;
2. 攔截器鏈:(Interceptor Chain)。攔截器鏈就是將攔截器按一定的順序聯結成一條鏈。在訪問被攔截的方法或欄位時,攔截器鏈中的攔截器就會按其之前定義的順序被呼叫。
3. 解釋:
	* 過濾器:是servlet規範中的一部分,任何java web工程都可以使用。
	* 攔截器:是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才能用。
	* 過濾器:是在url-pattern中配置了/*之後,可以對所有要訪問的資源攔截;
	* 攔截器:它是隻會攔截訪問的控制器方法,如果訪問的是jsp,html,css,image或者js是不會進行攔截的。
	* 它也是AOP思想的具體應用。
	* 我們要想自定義攔截器,要求必須實現:HandlerInterceptor介面。
4. 不同點:過濾器什麼都攔截,而攔截器只能攔截特定的;即過濾器的範圍大於攔截器;