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

**SpringMVC 筆記一 [共四篇,史上最全重點]*

SpringMVC 筆記一 [共四篇,史上最全重點] @記得關注哦,持續更新自己的最全最精華筆記哦~

今日內容:

1.注意: * 三層架構是java特有的。 表現層|持久層|業務層; * MVC設計模式是很多語言所採用的;[只要是web開發,都會了解到MVC的設計模式]

SpringMVC的開始:

1. 三層架構:平時是C/S或B/S架構,我們平時做web開發是用B/S架構的開發;服務端就經常分為三層架構;
	1. 表現層:框架[SpringMVC]:瀏覽器與伺服器端進行資料的互動;伺服器接收瀏覽器的引數,然後呼叫業務層的方法,拿到結果哦資料,然後響應給瀏覽器結果;
	2. 業務層:框架[Spring]:業務層與持久層進行互動,它響應表現層進行方法執行,並聯系持久層的該方法,拿到結果返回給表現層;
	3. 持久層:框架[Mybatis]:通過業務層的指令執行對資料庫的操作,拿到資料後返回給業務層;

2. MVC設計模型:
	1. M:model 模型  JavaBean  [mybatis,Spring]		-->執行者
	2. V:view	檢視  JSP		[jsp,html等頁面]	-->顯示
	3. C:Controller   控制器  Servlet   [SpringMvc]	-->接收請求完成響應
	
3. 概述:
	* 基於java實現的MVC設計模型的請求驅動型別的輕量級WEB框架,屬於SpringFrameWork的後續產品,已經融合在Spring web Flow裡面;
4. 優勢:
	1. 清晰的角色劃分	[模組開發]
		* 前端控制器	
		* 請求到處理器對映	[請求路徑]
		* 處理器介面卡		
		* 檢視解析器		[跳轉到xxx.jsp頁面]
		* 處理器或頁面控制器
		* 驗證器
		* 命令物件
		* 表單物件
		->>當請求進入SpringMVC中的時候,會經過這些一系列的模組,每個元件都會對這個請求體做一些事情,當走完的時候就直接返回結果;
		->>
			* 清晰明瞭,擴充套件修改容易,每個各司其職;
			* 可以使用命令物件直接作為業務物件
			* ...
5. SpringMVC和Struts2的優劣分析:
	1. 共同點:
		* 都是表現層框架,基於MVC模型編寫的
		* 底層都離不開原始ServletAPI
		* 處理請求的機制都是一個核心控制器
	2. 區別:
		* Spring MVC入口是Servlet,而Struts2是Filter    [Struts的核心就是過濾器]
		* SpringMVC 是基於方法設計的,Struts是基於類;struts每次執行都會建立一個類,所以SpringMVC會稍微比Struts2快些;		[SpringMVC是單例的,所以省去建立物件這個過程]
		* SpringMVC使用更加簡潔,同時還支援JSR303,處理ajax的請求更方便
		* Struts2的OGNL的表單式使頁面的開發效率相比Spring MVC更高些,但是執行效率並沒有JSNL表示式高;

SpringMVC的入門程式:

1. 需求分析: 編寫index.jsp超連結標籤,傳送請求給SpringMVC表現層的後臺,編寫一個類並編寫方法並返回結果,讓程式轉發到成功的jsp頁面上;
2. 入門實現案例:
	1. 開始:
		1. 需要搭建開發的環境
			* 構建Maven專案 jdk.1.8|骨架構建|字尾為:webapp、
			* cn.itcast|springmvc_day01_01_start
			* 下一步後新增一組鍵值對:[解決maven建立過慢] archetypeCatalog  |  internal
		2. 環境搭建:
			1. 在main下建立java包,resources包[Mark Diretory as 將java和resources的型別轉換]
			2. pom.xml引入:
				* 拷入依賴:
					*   <properties>        <spring.version>5.0.2.RELEASE</spring.version>   
					*    </properties>
				 
				 		<dependencies>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-context</artifactId>            <version>${spring.version}</version>        </dependency>
				 
				        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-web</artifactId>            <version>${spring.version}</version>        </dependency>
				 
				        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-webmvc</artifactId>            <version>${spring.version}</version>        </dependency>
				 
				        <dependency>            <groupId>javax.servlet</groupId>            <artifactId>servlet-api</artifactId>            <version>2.5</version>            <scope>provided</scope>        </dependency>
				 
				        <dependency>            <groupId>javax.servlet.jsp</groupId>            <artifactId>jsp-api</artifactId>            <version>2.0</version>            <scope>provided</scope>        </dependency>    </dependencies>
				 
			3. 配置前端控制器:	[servlet]
				1. 在web.xml 中新增:
					* <web-app>
					* <display-name>Archetype Created Web Application</display-name>
					* <servlet>
					* <servelt-name>dispatcherServlet</servlet-name>
					* <servelt-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
					*  <init-param>
					*     <param-name>contextConfigLocation</param-name>
					*      <param-value>classpath:springmvc.xml</param-value>
					*    </init-param>
					* <load-on-startup>1</load-on-startup>
					* </servlet>
					* <servelt-mapping>
					* <servlet-name>dispatcherServlet</servlet-name>
					* <url-prrten>/</url-pattern>
					* </serlvet-mapping>
					* </wen-app>
				2. 配置核心配置檔案:
					* 建立springmvc.xml	[resources下]   匯入其約束
		3. 開始入門程式
			1. 在index.jsp中編寫客戶端;
			2. 在java.cn.itcast.controller.HelloController中編寫控制器類:
				* public String sayHello(){
				* System.out.println("Hello StringMVC")
				* return "sucess";			//返回susess,就得提供這個名字的jsp名字
				* }	
			3. 將該類變成物件,即在springmvc.xml中開啟註解掃描:在文件中找
				* <context compontent...
			4. 在HeeloController上面新增@Controller註解[servelt標識]和@RequestMapping(path="/user")【一級目錄】,並在方法上添加註解@RequestMapping(path="/hello")  [請求對映,這個註解路徑hello就是以後該類該方法的請求路徑] 
			5. 讓mvc這個配置檔案載入,在前端控制器中配置載入,web.xml,提供初始化引數:
				* <init-param>
				* <param-name>contextConfigLocation</param-name>
				* <param-value>classpath:springmvc.xml</param-value>
				* </init-param>
			6. 建立一個名為sucess.jsp的成功介面
				* webapp.WEB-INF.pages.success.jsp
				* 在springmvc.xml配置檢視解析器:
					* <bean id="internalResourceViewResolver" class="org.spring...internalResourceViewResolver..."></bean>
					* <property name="prefix" value="/WEB-INF/pages">		//prefix  字首		
					* <property name="suffix" value=".jsp">			//suffix 字尾
					* </bean>
					* //第一個hellocontruller中的sayhello方法中返回sueccess,會通過檢視解析器新增前後綴和路徑,直接找到了動態包中的seccess.jsp頁面;可以直接訪問靜態頁面,但是WEB-INF裡面的內容只能請求轉發進去;
					* 很多公司就將一些登入後的頁面直接放到web-inf裡面,這樣就免去了很多的驗證操作;
				* 開啟springMVC框架註解的支援
				* <mvc:annotation-driven/>
			7. 補全兩個jsp網頁
				* <a href="hello" >入門程式</a>
				* 沒有虛擬路徑的時候,/hello或者hello都可以
				* 有虛擬路徑的時候
					* "/hello"  ->[絕對路徑] http://locathost:8080/hello
					* "hello"	->[相對路徑]	http://locathost:8080/springmvc-day01/hello
					->>  相對路徑是相對於當前檔案的路徑,會新增上虛擬路徑跳到相對路徑上;絕對路徑就會調到http://locathost:8080/hello,不會新增虛擬路徑地址了;
					* 但是在工作中我們會直接使用絕對路徑,使用相對路徑多次的跳轉容易出現問題
3. 解析:
	1. 啟動伺服器,載入一些配置檔案
		1. DispatcherServlet物件唄建立
		2. springmvc.xml被載入了
		3. HelloController建立成物件
	2. 傳送請求,後臺處理請求

	3. 流程分析
		1. 連線指向專案名稱/hello->DispatcherServlet[控制作用,指揮中心]->先找@RequestMapping(path="hello")sayHello方法執行,返回success,然後控制器找檢視解析器物件:InternalResourceViewResolver然後拼接前後綴和位置,找到succes.jsp然後返回結果;
		2. DispatcherServlet:它就是一個控制器,實際上沒有處理問題的能力,但是它可以排程其他的模組元件,完成任務;
		3. 元件功能:
			1. 處理器對映器:[HandlerMapping]--> 負責解析瀏覽器發來的地址,讓Controller類中方法去執行/hello   返回具體的哪個類和哪個方法
			2. 處理器介面卡:[HandlerAdapter]-->介面卡模式,讓過來的任何類和任何方法都轉為可識別的方法和類,然後進行執行操作;  //將千奇百怪的東西適配成格式統一的類,方法然後給處理器
			3. 處理器:[Handler]:處理器平常也叫作Controller  :拿到執行程式碼進行執行,然後給檢視解析器結果;
			4. 檢視解析器:如果是返回"success"等,就會呼叫檢視解析器進行解析,並跳轉到xxx.jsp頁面;
			5. View 檢視:接收到資料,將資料進行渲染,變成好看的頁面Jsp,Freemarker,Excel,Pdf等...	
			6. 處理器對映器,處理器介面卡,檢視解析器稱為SpringMVC的三大元件;

請求引數的繫結

1. @RequestMapping的引數內容:
	1. @AliasFor("path"): 這個註解的意思是別名  因為@RequestMapping裡的path和value值一樣,所以平時寫指定路徑的時候,可以不寫path,因為path=value,而value又可以省略;
	2. @RequestMapping()裡面的值:
		1. value:用於指定請求的URL。它和path屬性的作用是一樣的。		[指定傳送請求的路徑]
		2. method:用於指定允許的請求方式,不是此請求的方式將不被允許;	[指定傳送請求的方式]
		3. params={"username"}:表示瀏覽器傳送的請求必須有key值為username	[指定傳送請求的條件]
		4. params={"username=heihei"}:表示瀏覽器傳送的請求必須含有key值為username,對應的value值為heihei		[指定傳送請求的條件]
		5. headers={"Accept"}:表示瀏覽器傳送的請求頭必須包含Accept		[指定限制請求訊息頭的條件]
2. 請求引數繫結入門  [把表單提交的請求引數,作為控制器中方法的引數進行繫結]
	1. 繫結機制:
		1. 表單提交的資料都是k=v格式的。username=haha&password=123
		2. 把表單提交的請求引數,作為控制器中方法的引數進行繫結   [方法的引數名和表單中的key值是一樣的,那麼mvc就會自動的將表單的value值繫結到方法的引數上;]
		3. 要求:提交表單的name和方法引數的名字一樣;
	2. 請求引數的繫結:	[示例]
		1. 瀏覽器:
			* <a href="param/testParam?username=heihei&password=123"></a>		
			* //這裡不加/如果加了/則會是絕對路徑,如果加/必須提交上下文路徑,就是新增上虛擬路徑;
		2. 伺服器:ParamController類加註解:@Controller和@RequestMapping("param")
		3. 示例:
			* @Controller		//該註解交給容器管理,表名它是一個控制器
			* @RequestMapping()
			* public class ParamController{
			* //請求引數繫結入門
			* @RequestMapping("/testParam")
			* public String testParam(String username,String passowrd){
			* System.out.println("執行了");
			* System.out.println("username");
			* System.out.println("password");
			* return "succuess";
			* }};
		4. 傳入物件:
			1. 物件的封裝:
				1. 注意:如果方法內傳入的是物件,而請求傳入的是單一的資料,如果物件內的成員變數與request傳入的資料是一樣的,那麼資料會自動與傳入的物件繫結;
				2. 示例:
					1. 實體類
					* public Account implements Serializable{
					* private String username;
					* pricate String password;
					* private Date birthday;
					* get,set方法,to string方法...
					* }
					2. * 瀏覽器:
						* <form action="param/saveAccount" method="post">
						* 使用者名稱:<input type=""text" name="username"></br/>
						* 密碼:<input type=""text" name="password"></br/>
						* 金額:<input type=""text" name="birthday"></br/>
						* <input type=""submit" value="提交"></br/>
					3. 控制器:
						* @Controller		//該註解交給容器管理,表名它是一個控制器
						* @RequestMapping()
						* public class ParamController{
						* //請求引數繫結把資料封裝到javaBean中;
						* @RequestMapping("/saveAccount")
						* public String saveAccount(Account account){
						* System.out.println("執行了");
						* System.out.println("account");
						* return "success";
					* }};
			2. 引用型別的封裝:如果在實體類中還有一個引用物件:
				1. 實體類:Account
					* public Account implements Serializable{
					* private String username;
					* pricate String password;
					* private Date birthday;
					* private User user;
					* user 的get,set等方法...
					* get,set方法,to string方法...
					* }
				2. 實體類:User
					* private User implements Serializable{
					* private String uname;
					* private String age;
					* }
				3. 瀏覽器:
					* <form action="param/saveAccount" method="post">
					* 使用者名稱:<input type=""text" name="username"></br/>
					* 密碼:<input type=""text" name="password"></br/>
					* 生日:<input type=""text" name="birthday"></br/>		
					* user的名字:<input type=""text" name="user.uname"></br/>
					* user的年齡:<input type=""text" name="user.age"></br/>
					* <input type=""submit" value="提交"></br/>
3. 解決中文亂碼問題:
	1. 匯入過濾器即可;
	2. 如果web-app標籤報錯:
		* 將過濾器放在servlet配置檔案的前面
		* 匯入約束,提高版本;
	3. 配置檔案中的問題:
		1. classpath的意思:類路徑,就是編譯過後的類路徑
		2. <load-on-startup>1</load-on-startup>
			* 伺服器啟動的時候servlet不會被建立,它是在需要的時候第一次被建立,以後就不會再建立了;			[單例]
			* 只要加上此程式碼,則伺服器一啟動就會建立Servlet物件;在工作中,都會使用此,讓載入時間花在啟動中,也不要讓使用者停留等待;
		3. 物件中巢狀集合,集合中儲存物件,物件內含有成員資料;
			1. 瀏覽器:
			  * <form action="account/updateAccount" method="post"> 
			  * 使用者名稱稱:<input type="text" name="username" ><br/>
			  * 使用者密碼:<input type="password" name="password" ><br/>  
			  * 使用者年齡:<input type="text" name="age" ><br/>  
			  * 賬戶 1 名稱:<input type="text" name="accounts[0].name" ><br/>  
			  * 賬戶 1 金額:<input type="text" name="accounts[0].money" ><br/>  
			  * 賬戶 2 名稱:<input type="text" name="accounts[1].name" ><br/>  
			  * 賬戶 2 金額:<input type="text" name="accounts[1].money" ><br/>  
			  * 賬戶 3 名稱:<input type="text" name="accountMap['one'].name" ><br/>  
			  * 賬戶 3 金額:<input type="text" name="accountMap['one'].money" ><br/>  
			  * 賬戶 4 名稱:<input type="text" name="accountMap['two'].name" ><br/>  
			  * 賬戶 4 金額:<input type="text" name="accountMap['two'].money" ><br/>  
			  * <input type="submit" value="儲存">
			  * </form>
			2. 客戶端:
				* @RequestMapping("/updateAccount") 
				* public String updateAccount(User user) { 
				*  System.out.println("更新了賬戶。。。。"+user); 
				*  return "success"; 
				*  } 
4. 自定義型別 【在進行表單資料與控制器方法引數的繫結的時候,大多數的資料都可以進行自動轉換】
	1. String-->Integer    [常用型別可以自動轉換]
	2. 2000/11/11 -->Date	[可以轉換]
	3. 2000-11-11 -->轉換失敗,報錯~       [spring只認識/的日期,不認識/的日期]
		* 案例解決:
			1. 瀏覽器:
			* <form action="param/saveUser" method="post">
			* 使用者生日<input type="text" name="age" ><br/> 
			* <input type="submit" value="儲存">
			2. 控制器方法:
			* @RequestMapping("saveUser")
			* public String saveUser(User user){
			* System.out.println("user.date")
			* return "success";
			* } 
			3. 建立工具類:utils.StringToDateConverter
			* public class StringToDateConverter implements Converter<String,Date>{
			* @Overide
			* public Date convert(String source){
			* //傳入進來的字串
			* public Date convert(String source){
			* if(source=null){
			* throw new RuntimeException("請您傳入資料");
			* }
			* DateFormat df=new SimpleDateFormat("yyyy-MM-dd");
			* //把字串轉換成日期
			* try{
			* return df.parse(source);}
			* catch(Exception e){
			* throw new RuntimeException("資料型別轉換出現錯誤");
			* }} };
			
			4. 在springmvc.xml中配置自定義型別轉換器:
			* <bean id="conversionService" class="rg.springframework.context.support.ConversionServiceFactoryBean">
			* <property name="converters">
			* <set>
			* <bean class="cn.itcast.utils.StringToDateConverter">
			* </set>
			* </bean>
			5. 在springmvc.xml中配置支援:
				* <mvc:annotation-driven conversion-service="conversionService"/> 
				* //使用此配置,將開啟對自定義型別轉換器的支援;
	4. 使用此方法,只能解決使用-的問題可以識別日期,但是自動的/的辦法就不能使用了;
		1. 問題:如何使輸入日期2012/6/5 和2012-6-5都可以被識別;
			答:在工具中修改
				*  public class StringToDateConverter implements Converter<String,Date>{
				*  Date date=null
				* @Overide
				* public Date convert(String source){
				* //傳入進來的字串
				* public Date convert(String source){
				* if(source=null){
				* throw new RuntimeException("請您傳入資料");
				* }
				* DateFormat df=new SimpleDateFormat("yyyy-MM-dd");
				* //把字串轉換成日期
				* try{
				* return df.parse(source);}
				* catch(Exception e){
				* throw new RuntimeException("資料型別轉換出現錯誤");
				* }} };
		2. 中文亂碼解決:使用前面的程式碼只能解決post的中文亂碼問題,表單提交也基本使用psot,如果需要使get請求的亂碼問題也解決,可以使用tomcat 8以上或者網上搜索解決方案;	

常用註解

1.  原生API:
	* @RequestMapping
	* public String testServlet(HttpServletRequest request,HttpServletResponse response){
	* //拿到request
	* System.out.prinlnt(request);
	* //拿到session
	* HttpSession session=request.getSession();
	* //拿到ServletContext
	* ServletContext servletContext=session.getServletContext();
	* }
2. 常用註解:
	1. @RequsetParam:對方法的引數裡進行定義:必須傳入此屬性,如果不傳則報錯,瀏覽器傳送請求帶的資料必須與這個一樣;
		* public String testRequestParam(@RequestParam(name="name")String username){
		* System.out.println(username);
		* return "success";
		* };
	2. @RequestBody:用於獲取請求體的內容,直接使用得到的是key=value&key=value...結構的資料,它拿到的是整個請求體的內容;
		1. 示例:
			* @RequestMapping("/testRequstBody")
			* public String testRequestBody(@RequestBody String body){
			* System.out.println("執行了...");
			* System.out.println("body");
			* return "success";
			* }
		2. 作用範圍:JSON的資料在請求體裡,它可以與json配合使用;使用該註解可以直接拿到json的全部內容;
	3. @PathVaribale註解
		1. REST風格URL:
			1. 不是規範,不是強制要求使用,覺得好就用;
			2. 優點:

		2. restful程式設計風格
			1. 原來方式:
				* UserController類
				* @RequestMapping(path="/user/save")
				* save
				* 
				* @RequestMapping("/user/save")
				* update
				* 
				* @RequestMapping("/user/save")
				* findAll
			2. 現在的方式:
				* UserController類
				* path="/user" post
				* save
				* 
				* save="/user" put
				* update
				* 
				* path="/user" get
				* findAll
				* localhost:8080/user get
				* 
				* path="/user" get
				* fmdByID(id)
				* localhost:8080/user/{id} get
			3. 演示: 
				1. 演示控制類方法
				* @RequestMapping("/testPathVariable/{id}")
				* public String testPathVariable(@PathVariable(name="sid")String id){
				* System.out.println(id);
				* 
				* }
				2. 瀏覽器方法:
				* <a href="/testPathVariable/10">點我</a>
		4. 使用過濾器方式將請求進行轉換:
			1. WebClient類可以直接使用靜態方法傳送請求,並且可以模擬各種請求方式進行測試;
			2. 不是很重要,需要的話可以檢視筆記;
		5. 注意:
			* 使用相對路徑,又轉發到本頁面,會出現重複相對路徑,導致地址出錯,無法訪問;因為相對路徑的查詢其他路徑是從它本來相對路徑的基礎上進行查詢,就會重疊,如果查詢頁面的時候使用絕對路徑就不會出現這個問題了;
			* isELIgnored="false"  不忽略el表示式;
			* 示例:
			* <%@ page contextType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>

@記得關注哦,持續更新自己的最全最精華筆記哦~**