1. 程式人生 > 實用技巧 >RabbitMq常見的面試題

RabbitMq常見的面試題

javaWEB

2020/9/3 17:21:44


伺服器搭建

tomcat獨立環境 安裝 

	前置條件: 
		1.	環境變數中, 配置JAVA_HOME變數, 變數值為: JDK安裝的路徑, 切記不包含\bin
		2.	關閉防火牆, 解除安裝防毒軟體 解除安裝**安全管家 , 解除安裝 *60安全衛士 

	步驟:
		1.	下載tomcat  (官網- http://tomcat.apache.org/ )(下載tomcat85)
		2.	解壓到任意英文目錄下 . 
		3.	使用管理員身份, 進入dos命令列
		4.	在dos命令列中, 進入tomcat解壓目錄/bin
		5.	輸入安裝指令並回車
			service install
		6.	觀察安裝成功的提示資訊:
			the service 'tomcat8' has been installed;

執行tomcat , 通過網址訪問

	前置條件:
		tomcat預設埠號為 8080 , 將其修改為 8090
		1.	找到tomcat\conf\server.xml
		2.	在service.xml 大概70行的位置, 將8080更改為8090

	1.	開啟tomcat\bin\tomcat8w.exe
	2.	點選start啟動伺服器
	3.	在瀏覽器中, 通過ip:8090 來訪問網站

解除安裝tomcat:

方式1.	通過tomcat自身service命令 完成解除安裝
		1.	管理員身份, dos命令列進入tomcat\bin
		2.	執行解除安裝指令: service remove

方式2.	通過windows指令, 刪除服務
		1.	管理員身份, dos命令列輸入: sc delete 服務名

HTTP協議

超文字傳輸協議 , 是一個應用層的網路傳輸協議 . 屬於TCP/IP協議集中的一個子協議.

特點:
	1.	簡單 , 快速
	2.	無連線協議 ,  每次連線伺服器只處理一次客戶端的請求, 處理完畢, 立即斷開.
	3.	無狀態協議 ,  處理請求時 , 以及給客戶端回覆時. 沒有記憶能力 .
	4.	支援多種不同的資料提交方式, GET/POST等等
	5.	資料傳輸靈活, 支援任意資料型別.

HTTP協議的組成部分

由兩部分組成 
	1.	請求
		請求由四部分組成:
			1.	請求頭	 request header
					請求的頭部資訊 , 由一個個的鍵值對組成, 這些鍵值對描述的是客戶端的資訊.
			2.	請求體
					GET請求不存在請求體 ,將請求的引數儲存在網址中
					POST請求請求體的作用:	用於儲存客戶端請求的資料
			3.	請求空行
					請求頭部與請求體之間的一行空白行	
			4.	請求行
					由一個個的鍵值對組成, 這些鍵值對描述的是 請求的方式 ,訪問的網址, 以及http協議的版本.


	2.	響應
		響應由三部分組成:
			1.	響應頭
					響應頭部的資訊, 由一個個的鍵值對組成,  這些鍵值對描述的是伺服器的資訊.
			2.	響應體
					一個單獨的資料報, 是伺服器給客戶端回覆的主要內容.
			3.	響應行
					由一個個的鍵值對組成, 這些鍵值對描述的是 : 響應的狀態碼, 以及響應成功與失敗的提示


常見的響應狀態碼:

	200	:	成功
	404	:	資源不存在
	500	:	伺服器內部錯誤, (當程式出現異常時)

動態網頁技術

網頁會根據資料的不同, 展示不同的效果.

HttpServlet類

簡介:
	是JavaWeb體系中 三大元件之一.
	本質上, 就是一個執行在tomcat中的java類, 
	作用是:可以用來處理客戶端的請求, 以及對客戶端進行響應.

實現步驟:
	1.	編寫一個類 , 繼承自HttpServlet類
	2.	重寫父類的service(HttpServletRequest request,HttpServletResponse response)
	3.	在service方法中, 準備響應體.


將HttpServlet 對映到網址上 *****

	web3.0版本之前:
		步驟:
			1.	通過eclipse的javaEE 工具, 建立web.xml  (在3.0之前的專案建立時, web.xml預設是存在的)
			2.	向web.xml的根節點, 加入子節點:
					將Java類配置到tomcat中.
					<servlet>
						<servlet-name>給servlet起別名</servlet-name>
						<servlet-class>servlet的包名.類名</servlet-class>
					</servlet>
					給tomcat中的某個類, 新增網址 
					<servlet-mapping>
						<serlvet-name>要新增網址的別名</servlet-name>
						<url-pattern>/對映網址</url-pattern>
					</servlet-mapping>

			注意:
				如果對映網址為:  /demo1
				則瀏覽器訪問時:	http://ip地址:埠號/專案名稱/demo1

案例:
Java:
	public class Servlet1 extends HttpServlet{
		@Override
		protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
			//1.	設定響應內容的編碼格式, 以及內容型別
			response.setContentType("text/html;charset=utf-8");
			//2.	通過響應物件, 得到用於準備響應體的列印流
			PrintWriter out = response.getWriter();
			//3.	列印內容
			out.println("<h1>Hello JavaWeb</h1>");
			
		}
	}

web.xml
	<servlet>
		<servlet-name>haha</servlet-name>
		<servlet-class>cn.xdl.demo1.Servlet1</servlet-class>
	</servlet>
	<!-- 給tomcat中的某個類, 新增網址 -->
	<servlet-mapping>
		<servlet-name>haha</servlet-name>
		<url-pattern>/s1</url-pattern>
	</servlet-mapping>

web3.0版本 和 3.0版本之後:

通過註解來完成對映網址的配置 : 

	
案例:

	@WebServlet("/s2")
	public class Servlet2 extends HttpServlet {
	
		/**
		 * service方法, 表示服務方法
		 * 當用戶每次請求對應的網址時 , 此方法自動執行.
		 * 方法中的引數: 
		 * 		引數1.	HttpServletRequest  http協議中請求部分由tomcat進行了封裝, 封裝為了此物件, 物件中包含的是請求的相關資訊.
		 * 		引數2.	HttpServletResponse 
		 * 				http協議存在響應部分, 而響應的內容, 是由我們的程式碼所生成的 , 
		 * 				而HttpServletResponse , 它就是tomcat為了方便我們進行響應, 將響應的操作 ,封裝為了這個物件.
		 */
		@Override
		protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
			//1.	設定內容的型別 以及 編碼
			response.setContentType("text/html;charset=utf-8");
			//2.	通過響應物件 得到向響應體輸出的流
			PrintWriter out = response.getWriter();
			//3.	通過列印流, 向響應體中列印內容
			out
			.append("<html>")
			.append("<head><title>Servlet準備的網頁內容</title></head>")
			.append("<body>")
			.append("<h1>示例標題</h1>");
			
			for (int i = 0; i < 100; i++) {
				out.append("<div>嘿嘿嘿"+i+"</div>");
			}
			out
			.append("</body>")
			.append("</html>")
			;
			//4.	當此方法執行完畢後, 則tomcat認為 響應體準備完畢, 會將內容響應給客戶端.
		}

servlet的生命週期

指的是 Servlet從建立到消亡的週期

Servlet的建立時機:
	預設情況下, 當用戶第一次訪問Servlet的對映地址時, Servlet的物件被建立。
Serlvet的銷燬時機:
	當tomcat關閉, 或 應用被解除安裝時, Servlet被銷燬 
	(當tomcat關閉後 或 應用被解除安裝後, 使用者無法再訪問Servlet , 我們Servlet也就不需要在記憶體中存在了, 所以此時被銷燬)


tocmat為了方便我們 進行資源的快取 ,
為了讓我們能在Servlet建立時, 初始化一些重複利用的資源, 
為了讓我們能在Servlet銷燬時, 釋放這些重複利用的資源, 
提供了生命週期對應的方法 : 

	1.	init	方法:	當Servlet被建立時, 此方法執行,用於告知程式設計師, 第一次訪問產生了. 我們可以在此方法中初始化後續重複利用的資源.
	2.	service	方法:	服務方法, 當用戶每一次訪問時, 都會執行, 用於處理使用者的請求, 以及對使用者進行響應.
	3.	destroy	方法:	當servlet即將被銷燬時 , tomcat會呼叫此方法,  告知程式設計師. 我們可以在此方法中釋放初始化的那些重複利用的資源.

調整Servlet物件的建立時機 (可以理解為: 懶漢變餓漢)

使用web.xml的配置方式: 
	在servlet節點中, 加入子節點: load-on-startup 來調整啟動時機
	
	格式:

		<serlvet>
			<servlet-name></..
			<servlet-class>></...
			<load-on-startup>整型數字</load-on-startup>
		</servlet>

	load-on-startup: 
		取值為數字 , 預設值為-1:
			當值為負數時:			第一次請求時載入.
			當值≥0時 , 含義是: 伺服器啟動時 , 就載入servlet. 
								多個servlet之間值越小 越早載入. 值相同按照web.xml中自上而下的配置順序載入 

ServletConfig

是Servlet的配置物件, 每一個Servlet都擁有一個配置物件.
我們在web.xml中, 進行servlet的配置時 , 可以向Servlet中新增初始化的引數
這些引數, 會被儲存到一個ServletConfig 物件中.

 web.xml中配置的格式:
	<servlet>
		...
		...
		<!-- servlet節點中, 可以編寫n個init-param節點, 每一個init-param節點都表示一個鍵值對. -->
		<init-param>
			<param-name>鍵</param-name>
			<param-value>值</param-value>
		</init-param>
	</servlet>

從servlet中得到SerlvetConfig物件的格式:
	在Servlet類中, 可以通過兩種方式, 來獲取配置物件 , 這兩種方式在使用中, 是互斥的.
	
	方式1.	
		生命週期init方法中, 儲存ServletConfig 引數. 在方法中使用引數即可.

	方式2.
		在Servlet的任意程式碼位置, 通過getServletConfig()方法得到物件

從servletConfig物件中, 根據鍵得到值的格式:

	String value = config物件.getInitParameter(String name);

GET請求 與 POST請求的區別

GET請求:
	-	請求的引數 以多個鍵值對的形式儲存在網址中, 在網址的?後 , 鍵與值鍵值使用等號連線, 多個鍵值對之間&分割
	-	只能闡述字串型別的引數
	-	網址的最大長度為4kb , 通常支援的文字數量是: 最大2048 
	-	資料傳輸的時 不安全

POST請求:
	-	請求的引數 以多個鍵值對的形式儲存在單獨的資料包中 , 這個資料包叫做請求體.
	-	請求體中可以包含任意型別的資料, 例如: 圖片 . 音訊 等..
	-	資料大小, 理論上是無上限的.
	-	因為請求體是單獨的資料包, 所以較GET請求而言 安全;

什麼樣的請求 是 GET

以我們目前掌握的技術來說, 只有表單提交時method=POST , 請求方式是POST.  其他方式都是GET:

	-	瀏覽器輸入網址 , 回車
	-	點選超連結訪問
	-	表單提交時, method=GET
	-	通過js:  wondow.location物件, 進行替換與跳轉 
	-	ajax的get請求

什麼樣的請求 是 POST

-	表單提交時method=POST 
-	ajax的post請求 

如何接受請求的引數:

1.	根據一個name , 接收單個引數
		String value = request.getParameter(String name);

2.	根據一個name , 接收一組引數
		String[] values = request.getParameterValues(String name);

上述的兩個方法在獲取資料時,  name不存在, 則獲取的結果為null

請求亂碼的問題解決:

解決亂碼問題的兩種方式:

	方式1. 
		適用於tomcat8版本之前的GET請求亂碼解決, 以及所有版本的POST亂碼解決:

		解決亂碼的原理: 
				將文字亂碼的流程, 倒序執行一遍,得到正確的文字.
				倒序:	
					1.	將亂碼的文字,按照ISO-8859-1轉換為位元組陣列
					2.	將位元組陣列按照UTF-8的編碼轉換為文字

		格式:
			//1.	將亂碼的文字,按照ISO-8859-1轉換為位元組陣列
			byte[] bytes = 亂碼文字.getBytes("ISO-8859-1");
			//2.	將位元組陣列按照UTF-8的編碼轉換為文字
			String text = new String(bytes,"UTF-8");

方式2.
	適用於tomcat所有版本的post亂碼的解決.

	解決亂碼的原理:
		在使用請求體之前, 將請求體的預設編碼更改為utf-8.

	格式:
		//設定請求體的編碼,  一定要寫在獲取引數之前.
		request.setCharacterEncoding("UTF-8");

Servlet執行緒安全問題

Servlet的service方法,  在使用者每次請求時呼叫.
service方法的呼叫比較特殊:
	service方法的執行, 每一次都是在新的執行緒中.

因為service方法,  執行在新執行緒中,  有可能同時存在多個執行緒.  多執行緒操作時, 有可能發生執行緒安全問題.1


1.	靜態的同步方法的 同步鎖  --> 類.class 這個物件

2.	非靜態的同步方法的 同步鎖 --> this物件

3.	同步程式碼塊的 同步鎖 --> 程式設計師使用時 提供.


案例:

	@WebServlet("/s1.do")
	public class Servlet1 extends HttpServlet {
		//餘票
		private int count = 10;
		protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
			synchronized(this) {
				if(count>0) {
					//有票
					System.out.println("有票, 正在出票");
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					count--;
					System.out.println("出票完成, 餘票:"+count);
				}else {
					System.out.println("很遺憾 ,沒有餘票了");
				}
			}
			response.getWriter().append("ok");
		}
	}

請求的轉發

概念:	將一個web元件 為處理完畢的請求, 通過tomcat轉交給另一個web元件處理.

步驟:
		1.	獲取請求轉發器
				RequestDispatcher rd = request.getRequestDispatcher("轉發的地址");

		2.	通過轉發器 發起轉發
				rd.forward(請求物件 , 響應物件);

簡寫步驟:
		 request.getRequestDispatcher("轉發的地址").forward(請求物件 , 響應物件);

原理(tomcat內部執行流程):
		1.	當瀏覽器訪問伺服器中的tomcat時.
		2.	tomcat將協議中的請求進行封裝, 封裝為HttpServletRequest物件 , 將響應封裝到HttpServletResponse物件中
		3.	找到對應對映地址的Servlet , (第一次尋找會建立物件) , 呼叫物件的service方法, 傳入請求與響應物件.
		4.	在serlvet中, 我們可以控制將請求轉發到另一個地址 , 
		5.	tomcat接收到請求轉發指令後, 將原請求物件 和 新的響應物件傳給轉發的新地址.
		6.	此時舊的響應物件就失效了, 無法在進行響應了. 由新的地址的響應物件 負責給使用者進行響應.
		7.	新地址準備完畢響應體, 傳送給瀏覽器
特點:
	1.	轉發的過程中, 只發生了一次請求, 多個Servlet之間共享一份請求資訊.
	2.	轉發無法跨域實現 ( 無法跨網站進行轉發, 例如: 京東無法轉發給淘寶.)
	3.	無論轉發過程中, 觸發了多少個web元件 , 對於瀏覽器而言, 它能感知到的 只是發起了一次請求, 接收到了一次響應.
	4.	轉發過程中, 瀏覽器地址不會發生改變
	5.	相較於重定向而言, 效率高.

請求的重定向

概念:	在進行響應時, 告知瀏覽器新的請求地址, 瀏覽器接到後,自動發起新的請求找指定的地址.

步驟:
		response.sendRedirect("重定向的新地址");

原理(執行流程):
		1.	當瀏覽器請求某個Servlet時.  
		2.	Servlet對瀏覽器響應一個302的狀態碼 ,  以及一個 鍵為location的值, 這個值表示新的地址;
		3.	當瀏覽器接收到302的狀態碼以後, 會自動尋找lcoation的值, 併網頁自動跳轉到location的值表示地址上 !
特點:
	1.	重定向會產生新的請求 和 新的響應.
	2.	使用重定向,  可以跨域實現 (可以跨網站操作 ,例如: 京東可以將請求重定向到淘寶)
	3.	瀏覽器地址會發生改變 , 顯示的是重定向的地址;
	4.	相較於請求轉發而言, 效率較低.

注意:

1.	在使用者的一次訪問過程中, 我們可以進行無限次的轉發/重定向 ! 但是記住一點: 在多次轉發/重定向的操作中, 一定要有出口 !
2.	不只是可以將請求轉發/重定向到servlet上, 任何的web資源都可以接受轉發 和 重定向. 
3.	當我們的程式碼進行了轉發/重定向時 , 相當於將響應的操作交給了其他資源. 那麼在進行轉發和重定向的程式碼後面, 就不要再編寫響應的操作了. 因為無效. 

HttpServletRequest 類的常用操作

1.	獲取客戶端的ip地址:
		String ip = request.getRemoteAddr();

2.	獲取客戶端訪問的地址:
		String url = reuqest.getRequestURI();

3.	獲取tomcat執行的ip地址
		String ip = request.getServerName();

4.	獲取tomcat執行的埠號
		String port = request.getServerPort();

5.	獲取請求方式
		String method = request.getMethod();	

6.	獲取get請求的?後的引數列表
		String params = request.getQueryString();	

HttpServletResponse 類的常用操作

1.	設定響應的內容型別(網頁) 以及 編碼格式(UTF-8)
		response.setContentType("text/html;charset=utf-8");

2.	設定響應的內容編碼格式 (常用於響應JSON資料)
		response.setCharacterEncoding("UTF-8"); 

3.	響應錯誤碼給客戶端
		response.sendError(int status,String msg);

ServletContext 物件 (Servlet上下文)

每一個Servlet都是一個獨立的網頁地址 , 它們之間無法進行通訊以及交流 ,
例如:	我們想統計網站的訪問次數 , 每一個servlet只能統計自己被訪問的次數, 無法彙總.

Servlet上下文物件, 就是用於Servlet之間資訊共享的, 是多個servlet之間通訊的橋樑 
Servelt上下文物件, 在專案的執行過程中, 只有一個.每一個Servlet獲取的上下文物件, 都是同一份.
Servlet上下文物件, 就像一個Map集合, 可以儲存n個鍵值對資訊!
如何得到專案的Servlet上下文物件
格式:
	ServletContext context = getServletContext();
上下文物件常用方法 熟悉
1.	儲存資料
		context.setAttribute(String name,Object value);
2.	獲取資料
		Object value = context.getAttribute(String name);
3.	刪除資料
		context.removeAttribute(String name);

4.	獲取專案執行時的資料夾 絕對路徑.
		String path = context.getRealPath("/");

會話跟蹤技術 (狀態管理)

Http協議是無狀態的 , 沒有記憶能力.
在瀏覽器與伺服器互動時, 因為無狀態的特性, 會導致無法連貫的互動.

HTTP協議實現狀態管理, 有兩種方式:

	1.	Cookie技術		:	將互動時產生的狀態 儲存在客戶端中.
	2.	Session技術		:	將互動時產生的狀態 儲存在伺服器中.

Cookie技術

技術步驟, 以及原理:

	1.	當伺服器向客戶端響應時,  可以向響應頭中加入Cookie , 每一個Cookie表示一個鍵值對
	2.	當瀏覽器接收到響應頭中的Cookie後, 會將其儲存在本地的一個文字檔案中, 
	3.	當瀏覽器再次訪問相同的伺服器時,  會去文字檔案中尋找這個伺服器之前儲存的Cookie 
	4.	將尋找到的Cookie 攜帶到請求頭中, 傳送給伺服器.


如何建立一個Cookie 
	Cookie 在Java程式中的體現 是一個表示鍵值對的 Java類. 類名為Cookie

	格式:
		Cookie  cookie  = new Cookie(String name,String value);

如何將建立的Cookie 新增到響應的頭部 

	通過響應物件, 將Cookie 新增到響應的頭部

	格式:
		response.addCookie(Cookie cookie);

	一次響應, 可以新增0-n個Cookie  , 
	瀏覽器接收後, 會儲存到文字檔案中 , 如果相同域的相同路徑 儲存相同鍵的Cookie , 會導致舊值被覆蓋.



如何從請求頭部 得到 我們之前儲存的多個Cookie 

	可以從請求物件中, 得到之前儲存的Cookie資訊, 得到的是一個Cookie陣列 , 如果從未儲存過Cookie ,則得到的資料是null
	
	格式:
		Cookie[] cookies = request.getCookies();

如何調整Cookie的存活時長 

	cookie.setMaxAge(int 秒);

	傳入的值:
		-	負數		:	預設值為-1, 負數表示瀏覽會話結束時 刪除. (指的是瀏覽器關閉)
		-	正數		:	存活的秒數
		-	0		:	存活的秒數, 通常使用者覆蓋一個Cookie ,來完成刪除操作.


Cookie儲存時的路徑問題 

	因為Cookie傳送時, 需要匹配域 和路徑 . 
	我們在編寫專案時, 經常因為路徑不同, 導致cookie無法讀取. 

	Java為我們提供了設定Cookie路徑的方法, 我們可以在任意的servlet中, 將Cookie路徑設定為一致, 來儲存和讀取.

	格式:
		在cookie新增到響應頭部之前 設定:
		cookie.setPath("/");


Cookie技術的優缺點: 

	缺點:
		1.	Cookie儲存的資料型別有限制, 只能儲存字串 		( 早期無法儲存中文 )
		2.	資料儲存的大小有限制, 不能超過4kb (4096個位元組)	
		3.	資料儲存在使用者的計算機的 文字檔案中 , 不安全, 有可能被惡意程式讀取.
		4.	受限於使用者的瀏覽器設定,  當瀏覽器禁用Cookie時, cookie就無法使用了.
		
	優點:
		資料儲存在客戶端中, 分散了伺服器的壓力. 

Session技術

技術步驟, 以及原理

	1.	瀏覽器訪問伺服器時,  伺服器可以主動建立Session物件 , 一個session表示一個鍵值對的容器 ,類似Map集合
	2.	每一個Session建立時, 會產生一個id , 這個id會儲存到一個Cookie中, 併發送給瀏覽器
	3.	等瀏覽器下一次訪問時, 會攜帶Cookie, cookie中包含session的id , 
	4.	我們就可以根據得到的sessionid , 從伺服器中尋找到屬於這個瀏覽器的session物件.

如何獲取session物件

格式1. ****
		呼叫請求的獲取session的方法 (無參) , 方法的內部呼叫了一參方法, 傳入了true
		HttpSession session = request.getSession();

格式2.
		呼叫請求的獲取session的方法 (一參)
		HttpSession session =  request.getSession(boolean isNew);

		引數的含義:
			true:	根據瀏覽器發來的sessionid 尋找session物件並返回, 如果不存在 ,則建立新的並返回
			false:	根據瀏覽器發來的sessionid 尋找session物件並返回, 如果不存在 ,則返回null

session的常用方法

1.	儲存資料
		session.setAttribute(String name,Object value);
2.	取出資料
		Object value = session.getAttribute(String name);
3.	刪除資料
		session.removeAttribute(String name);
4.	銷燬這個Session
		session.invalidate();

session的存活時長

session的預設存活時長為30分鐘, 
當用戶的上一次訪問 距離現在已經超過30分鐘時,  session會自動銷燬.

設定session的儲存時長:

	方式1.	修改單個session的時長:
				session.setMaxInactiveInterval(int 秒);

	方式2.	修改tomcat下, 所有session的預設時長
				獨立環境:	找到conf/web.xml檔案
				開發環境:	找到servers/web.xml

				修改其中的session-config節點
				
				案例:
					<session-config>
						<session-timeout>數值分鐘</session-timeout>
					</session-config>

session的優缺點

優點:
		1.	資料儲存在伺服器中, 安全
		2.	session中可以儲存任意型別資料
		3.	儲存的資料大小, 理論上是無限制的.
缺點:
		資料儲存在伺服器中, 大量的使用者儲存session時, 會對伺服器造成極大的壓力, 極易導致伺服器資源耗盡.

Cookie技術 和 Session技術 不是互斥的.

Cookie和session 我們是結合使用的.

	對於安全無要求的字串資料, 儲存在cookie中
	對於安全敏感的資料, 儲存在session中
	對於安全敏感 , 且較大資料, 儲存在資料庫中...

JSP簡介

Java Server Pages Java的動態網頁技術

JSP引擎

引擎原理:

	JSP引擎用於將JSP檔案, 轉換為Servlet

原理步驟:
	1.	在伺服器啟動時, JSP引擎讀取JSP檔案
	2.	將檔案轉換為Servlet , 並給Servlet新增對映地址為 原JSP 檔名稱.
	3.	當用戶訪問  xxxx.jsp時, 請求的不是jsp檔案, 而是JSP引擎根據檔案轉換的Servlet

JSP語法結構 *

JSP檔案儲存在 .jsp檔案中 .  儲存的路徑:  webContent目錄下.

JSP語法存在三大語法結構:

	1.	HTML程式碼
	2.	Java程式碼
	3.	JSP特有的一些語法結構.


Java程式碼宣告區 

	指的是 Java類的成員位置 , 在JSP檔案中的Java程式碼宣告區中編寫的Java程式碼, 會原封不動的生成到Servlet的成員位置!


	語法格式:
		<%!
			編寫宣告區的Java程式碼
		%>

Java程式碼執行區 

	指的是Servlet的service方法. 用於每次請求都會執行

	語法格式:
		<%
			編寫Java邏輯程式碼,  生成到service方法中
		%>

輸出表達式 

	用於快速的將Java程式碼中的變數, 輸出到網頁中

	語法格式:
		<%=變數名 %>

	上述語法格式中的值, 其實是被生成到了out的print方法中,

	例如:			<%=count  %>
	生成的Servlet:	out.print(count);

JSP中的註釋:

因為JSP包含了三種語法結構.

所以三種結構的註釋, 都可以使用.

HTML註釋:
	語法:	<!-- 註釋 -->
	注意:	在JSP中, 只能編寫在HTML程式碼的位置 ,只能用於註釋HTML部分. 
			編寫的HTML註釋會被JSP引擎認為是HTML程式碼 , 轉換為:	out.write("<!-- 註釋 -->");
	
Java註釋:
	語法:	//單行  /*多行*/ /**文件*/
	注意:	在JSP中, 只能編寫在Java程式碼的位置, 只能用於註釋Java部分.
			編寫Java註釋會被JSP引擎認為是Java程式碼, 原封不動的轉換到Servlet中

JSP註釋:
	語法:	<%-- JSP的註釋 --%>
	注意:	指的是JSP檔案的註釋, 被JSP註釋的內容, 在JSP引擎轉換時期被忽略.

JSP三大指令

指令的格式:
	<%@ 指令名稱 屬性名1=值 屬性名2=值 ... 屬性名n=值 %>

page指令

完整格式:
	<%@ page
		language="java"
		contentType="text/html;charset=utf-8"
		pageEncoding="UTF-8"
		extends="繼承的類"
		buffer="數字|none" -- 是否允許快取, 以及允許快取的大小. 預設快取, 大小為8kb
		autoFlush="true|false" -- 緩衝器是否自動清除, 預設為true
		session="true|false" -- 是否在service方法中,  提前建立好session , 預設true
		isThreadSafe="true|false" -- service方法是否是執行緒安全的. 預設false
		errorPage="網址" -- 當JSP中的程式碼出異常時,  頁面自動跳轉到網址 ,通常用語提示BUG.  **
		isErrorPage="true|false" -- 用於處理錯誤的頁面 , 通常isErrorPage=true的頁面, 會被其他頁面通過errorPage引入.  **
								 --	為true時, 會在service中提前準備好, 異常物件 exception
		import="導包列表" -- 屬性值是導包的內容,  多個包之間使用逗號隔開 ** 
	%>

指定專案全域性錯誤碼的 處理頁面 *

步驟:
	1.	先開啟專案的 web.xml檔案
	2.	在根節點中, 加入子節點:
			<error-page>
				<error-code>錯誤碼</error-code>
				<location>處理的頁面地址</location>
			</error-page>

	注意:	error-page可以編寫多個

	例如:
			<error-page>
				<error-code>404</error-code>
				<location>/error.jsp</location>
			</error-page>
			<error-page>
				<error-code>500</error-code>
				<location>/error.jsp</location>
			</error-page>

include指令

用於將一個JSP 或 HTML檔案, 引入到另一個JSP檔案中

格式:	<%@ include file="地址" %>

include動作

用於將一個JSP或HTML檔案 引入到另一個JSP檔案中.

格式:	<jsp:include page="地址" flush="true" />

include 指令與動作的區別

include指令:	在JSP的轉換時期 , 將引入的JSP檔案嵌入到了include指令的位置 (合併為了一個檔案), 然後轉換為Servlet ! 最終生成的是一個.java檔案
include動作:	在JSP程式的轉換時期, 引入的JSP檔案會獨立轉換為Servlet , 到訪問時, 在將響應體合併, (將被引入檔案的響應體 , 動態載入在檔案中) !

JSP內建物件(隱含物件)

內建物件指的是:	JSP引擎在轉換時期 , 在service方法的前面, 幫我們提前準備好的一些物件. 我們在<%%>程式碼塊中, 可以直接使用它們.

作用:
	這些物件包含了我們進行動態網頁開發 常用的一些物件. 提供了大量的便於我們開發的功能, 可以簡化我們的開發過程.

九大內建物件

1. * 
	物件名	:	request
	型別		:	HttpServletRequest
	作用		:	請求物件, 包含了請求相關的資訊 !

2. *
	物件名	:	response
	型別		:	HttpServletResponse
	作用		:	響應物件, 包含了一些用於響應的功能

3. *
	物件名	:	pageContext
	型別		:	PageContext
	作用		:	頁面的上下文 ,  用於獲取其它8大內建物件. 

4. *
	物件名	:	session
	型別		:	HttpSession
	作用		:	會話物件 , 用於會話跟蹤 與 狀態管理

5. *
	物件名	:	application
	型別		:	ServletContext
	作用		:	Servlet的上下文 , 一個應用程式啟動中, 只會存在一個Servlet上下文, 用於多個Servlet之間的資料共享與通訊.

6. 
	物件名	:	out
	型別		:	JSPWriter
	作用		:	列印流, 用於向響應體中輸出資料.

7.
	物件名	:	config
	型別		:	ServletConfig
	作用		:	Servlet的配置物件, 用於初始化一些鍵值對資訊

8.
	物件名	:	page
	型別		:	Object , 但是在賦值時, 使用的是this . 所以本質上是當前JSP轉換的Servlet類的型別.
	作用		:	指當前頁面自身 .

9.
	物件名	:	exception
	型別		:	Throwable
	作用		:	當頁面的page指令: isErrorPage="true"時, 才會存在的一個物件.
				當別的頁面發生異常後, 跳轉到此頁面時 , exception是發生的異常物件.

JSP的四大域物件

九大內建物件中, 包含四個較為特殊的物件. 這四個物件我們稱其為:		域物件! 
域物件存在一個特點: 
	都包含儲存資料 / 刪除資料 / 獲取資料的方法 , 所謂的域, 指的是儲存的資料使用的作用域.
	
	儲存資料:
		setAttribute(String key,Object value);
	獲取資料:
		Object value = getAttribute(String key);
	刪除資料:
		removeAttribute(String key);

四大域物件:
	-	pageContext (一個JSP頁面的域物件) 
			頁面的上下文 , 儲存在pageContext中的資料 , 域是最小的, 只有當前請求的當前頁面才可以使用. 頁面響應完畢物件就被垃圾回收了. 
	-	request (一次請求域物件)
			請求物件 , 儲存在request物件中的資料, 只有當前請求 , 才可以使用 ! 一次請求可能發生多次轉發 ,所以有可能跨越多個Servlet/JSP頁面.
	-	session (一次會話域物件)
			會話物件, 儲存在session物件中的資料, 在當前會話中可以使用. 一次會話可能包含多次請求.
	-	application (一次服務域物件)
			Servlet上下文物件, 儲存在application中的資料, 在伺服器關閉之前都可以使用.  
			應用的一次啟動, 伺服器中只會存在一個application物件, 一次伺服器啟動中可能包含多次會話.

EL表示式

作用, 用於快速的從域物件中 取出資料, 並將結果輸出到網頁中 .

格式:
	${ 表示式 }

EL表示式的使用

1.	用於運算

	${1+2+3+4+5}	輸出的結果是:	15

2.	用於取出域物件中的資料  

	訪問儲存的資料格式: ***
		${儲存的key}

	訪問儲存的物件的屬性:
		格式1.	${儲存的key.屬性名} ***
		格式2.	${儲存的key["屬性名"]}
		格式3.	
			動態取值:	
				指的是按照域物件中儲存的一個屬性名, 以及物件名, 從物件中取出屬性值
			
				${儲存的物件key[屬性名的key]}

3.	用於取出域物件中的集合/陣列中的物件屬性	
		格式1.	${儲存的key[下標].屬性名}
		格式2.	${儲存的key[下標]["屬性名"]}
		格式3.	
			動態取值:	
				指的是按照域物件中儲存的一個屬性名, 以及物件名, 從物件中取出屬性值
			
				${儲存的物件key[下標][屬性名的key]}

EL表示式, 取出資料的流程

尋找的順序:	從域範圍小的 到 域範圍大的	

步驟:

	1.	先從pageContext中, 尋找資料是否存在.
	2.	如果pageContext中不存在資料, 則去request中尋找資料是否存在.
	3.	如果request中不存在資料, 則去session中尋找資料是否存在.
	4.	如果session中不存在資料, 則去application中尋找資料是否存在
	5.	如果application中不存在資料, 則向網頁輸出 :  ""

在上述的步驟中, 一旦尋找到資料, 則流程結束, 將尋找到的資料輸出到網頁中.

taglib 指令 熟悉

用於在JSP中, 引入標籤庫.

語法格式:
	<%@ taglib prefix="短名稱" uri="地址"%>


屬性:
	prefix	:	是引入的標籤庫的名稱, 用於區分引入的多個標籤庫 , 
				在使用標籤庫中的標籤時 , 需要在前面加入庫名:

	uri		:	引入標籤庫的操作 與 網頁中引入其他靜態資源不一樣. 
				引入靜態資源(圖片,音樂等等) , 使用相對路徑.
				每一個標籤庫都擁有uri屬性, 引入標籤庫, 不需要路徑, 只需要匹配uri就可以

JSTL標籤庫的使用

JSTL 是一套JSP的標準標籤庫
IF標籤
格式
	<短名稱:if test=""></短名稱:if>
	test值:	boolean值 , 可以是具體的boolean值, 也可以是el表示式運算的結果

	如果test值為true , 則內容顯示
	如果test值為false , 則內容不顯示 (內容不會通過流, 輸出到響應體中)

案例:
		<heihei:if test="${ flag }">
			從前有一座山 , 山上有座尼姑庵 , 庵裡有個老尼姑 和 一個小姐姐 . 
		</heihei:if>

choose + when + otherwise

類似Java中的:	switch + case + default

作用:
	用於多分支, 顯示.

案例:
	<%
		pageContext.setAttribute("flag",6);
	%>
	<heihei:choose>
		<heihei:when test="${ flag==1 }">孔子東遊 , 見兩小兒辯日</heihei:when>
		<heihei:when test="${ flag==2 }">千呼萬喚始出來</heihei:when>
		<heihei:when test="${ flag==3 }">白日依山盡</heihei:when>
		<heihei:when test="${ flag==4 }">日照香爐生紫煙</heihei:when>
		<heihei:when test="${ flag==5 }">萬里江陵十日還</heihei:when>
		<heihei:otherwise>
			停車坐愛楓林晚
		</heihei:otherwise>
	</heihei:choose>


forEach 標籤

	用於遍歷集合或陣列元素

	格式:
		<標籤庫名稱:forEach items="" var="">
		
		</標籤庫名稱:forEach>

	屬性:
		items	:	使用el表示式, 從域物件中取出的要遍歷的陣列或集合物件.
		var		:	在進行遍歷時, 陣列或集合中的每一個元素被取出後, 會單獨儲存在pageContext中, var的值就是儲存時的鍵

	案例:
		
		<%
			ArrayList<String> data = new ArrayList<>();
			data.add("孔子東遊 , 見兩小兒辯日");
			data.add("千呼萬喚始出來");
			data.add("白日依山盡");
			data.add("日照香爐生紫煙");
			data.add("萬里江陵十日還");
			data.add("停車坐愛楓林晚");
			data.add("老驥伏櫪志在千里");
			pageContext.setAttribute("data", data);
		%>
		<heihei:forEach items="${data }" var="str">
			<h1>${ str }</h1>
		</heihei:forEach>

自定義標籤庫

步驟:
	1.	編寫一個類, 繼承SimpleTagSupport 類
	2.	重寫類的doTag方法 ,
	3.	在doTag方法中, 通過getJspContext() 方法 得到JSP的上下文.
	4.	通過上下文物件, 可以得到其他8大內建物件 , 通常我們是getOut()得到輸出流, 向網頁輸出內容.

	5.	編寫tld檔案, 描述標籤庫和標籤.


案例:
	Class:
		public class MyTag extends SimpleTagSupport {

		private static ArrayList<String> data = new ArrayList<>();
		static {
			data.add("理想是人生的太陽。 —— 德萊賽");
			data.add("真實是人生的命脈,是一切價值的根基。 —— 德萊塞");
			data.add("真的猛士,敢於直面慘淡的人生,敢於正視淋漓的鮮血。 —— 魯迅");
			data.add("人生須自重。 —— 黃宗羲");
			data.add("在人生的道路上,當你的希望一個個落空的時候,你也要堅定,要沉著。 —— 朗費羅");
			data.add("真理惟一可靠的標準就是永遠自相符合。 —— 歐文");
			data.add("竹子用了4年紮根,第五年一出頭就被做成竹筍炒肉。");
			data.add("公主病的成因沒別的,不是醜就是窮。“那有錢又漂亮脾氣卻不好的呢?”那本來就是公主,不叫病。");
			data.add("所謂的女漢子,只不過是因為長得醜而已,但凡有些爺們氣質的漂亮姑娘,都被稱為女王大人。");
			data.add("騰不出時間來睡覺的人,遲早會騰出時間來生病;騰不出時間來戀愛的人,遲早會騰出時間來相親。");
			data.add("快樂分享錯了人就成了顯擺,難過分享錯了人就成了矯情。");
			data.add("請記住,你並不是一無所有,你還有病!哈哈");
			data.add("魚和熊掌不可兼得,但單身和窮可以!");
			data.add("用錢當然買不到快樂,但只要你有錢,別人會想辦法讓你快樂。");
			data.add("你才二十多歲,沒有遇到對的人很正常,以後你就知道,大概遇不到了!");
			data.add("這年頭,當個宅男宅女,也得先買房子!");
			data.add("能背後給你一刀的,往往是你信任的!");
			data.add("失敗並不可怕,可怕的是你還相信這句話。");
			data.add("比一個人吃火鍋更寂寞可憐的是,一個人沒有錢吃火鍋。");
			data.add("你只知道人家化妝比你好看,卻不知道,她們卸了妝,不僅比你好看,面板還吹彈可破。");
			data.add("從前車馬很慢,書信很遠,一生只夠愛一個人,但是能納很多妾。");
			data.add("歲月告訴我,除了快遞,我啥也等不到!");
			data.add("假如今天的你被生活辜負了,別傷心,因為明天生活還會繼續辜負你!");
		}
		
		@Override
		public void doTag() throws JspException, IOException {
			JspContext context = getJspContext();
			JspWriter out = context.getOut();
			String text = getText();
			out.print(text);
			
		}
		public String getText() {
			Random r = new Random();
			int i = r.nextInt(data.size());
			return data.get(i);
		}
	}




	TLD檔案:

	<?xml version="1.0" encoding="UTF-8" ?>
	<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
	    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
	    version="2.0">
	  	<!-- 註釋 ,描述  -->
	  	<description>這是兄弟連Java30期自定義的一個標籤庫</description>
	  	<!-- 名稱 -->
	  	<display-name>XDL</display-name>
	  	<!-- 版本 -->
	  	<tlib-version>5.21</tlib-version>
	  	<!-- 短名稱 -->
	  	<short-name>x</short-name>
	  	<!-- 用於匹配的uri -->
	  	<uri>http://hahaha.heiheihei.com</uri>
	  	<!-- 在標籤庫中 一個tag節點 , 表示庫中的一個自定義標籤 -->
		<tag>
			<!-- 標籤的描述 ,在使用時, 會提示 -->
			<description>這個標籤會向網頁中輸出一條雞湯句子</description>
			<!-- 標籤的名稱 -->
			<name>djt</name>
			<!-- 標籤的類的全名 -->
			<tag-class>cn.xdl.tag.MyTag</tag-class>
			<body-content>empty</body-content>
		</tag>
	</taglib>

web三大元件

1.	Servlet
2.	Filter	過濾器
3.	Listener 監聽器

Filter 過濾器

所謂的過濾. 指的是過濾請求.
有時我們進行後段專案開發時, 有些請求 ,需要特定的條件才能操作 我們可以通過Filter , 來過濾不滿足的使用者操作.
例如:	
	用於的個人中心, 應該是登入	後才可以檢視的.
	當用戶請求個人中心頁面時, 我們就可以編寫過濾器, 將未登入的所有使用者過濾掉, 並重定向至登入頁面.

採用了面向切面程式設計思想(AOP) .

使用步驟:

	1.	編寫一個類 , 實現Filter介面
	2.	通過web.xml 或 註解的方式, 配置過濾器的過濾地址. 

案例:

	public class ParamFilter implements Filter {
		/**
		 * 當發生匹配過濾地址的請求時, doFilter執行
		 * 過濾器執行在web的所有資源之前 , 預設此方法是攔截請求的, 
		 * 如果需要放行 , 需編寫: chain.doFilter(request,response);
		 */
		@Override
		public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
				throws IOException, ServletException {
			
			HttpServletRequest req = (HttpServletRequest) request;
			HttpServletResponse resp = (HttpServletResponse) response;
			if(req.getParameter("a")==null) {
				resp.setContentType("text/html;charset=utf-8");
				//攔截
				resp.getWriter().append("<h1>很遺憾, 引數錯誤, 無法訪問</h1>");
			}else {
				//放行
				chain.doFilter(request, response);
			}
		}
	}


web.xml

  <filter>
  	<filter-name>pf</filter-name>
  	<filter-class>cn.xdl.demo1.ParamFilter</filter-class>
  </filter>
  <filter-mapping>
  	<filter-name>pf</filter-name>
  	<url-pattern>/home.jsp</url-pattern>
  </filter-mapping>

過濾器鏈 

	當多個過濾器, 過濾地址重複時 , 就形成了過濾器鏈 . 使用者的請求, 需要過濾器鏈中的所有過濾器放行, 才可以正常訪問.

	過濾器鏈執行的順序: 
		web.xml中的順序: 按照web.xml中配置的先後順序, 來執行. 

		註解配置的順序:	 按照類名的自然排序, 順序執行. 

		web.xml中配置的過濾器, 一定執行在註解配置過濾器的前面.

Listener

監聽器 , 監聽是伺服器的一些事件 . 

兩類事件:
	1.	伺服器中元件的生命週期相關事件. 
	2.	域物件中資料的變化事件 . 


ServletContextListener 

	用於監聽Servlet上下文的建立與銷燬.  

	案例:
		@WebListener
		public class MyServletContextListener implements ServletContextListener {
		
			/**
			 * 當上下文物件 即將銷燬時, 方法執行
			 * 上下文物件銷燬的時機, 是 伺服器關閉或專案被解除安裝. 所以我們常在這裡進行全域性資源釋放的操作. 
		     */
		    public void contextDestroyed(ServletContextEvent arg0)  { 
		    	System.out.println("專案關閉了");
		    }
		
			/**
			 * 當上下文物件 被建立初始化時  , 這個方法執行
			 * 因為上下文物件建立的時機是 伺服器中專案啟動時  , 所以我們常在這裡進行全域性資源的初始化操作.
		     */
		    public void contextInitialized(ServletContextEvent arg0)  { 
		    	System.out.println("專案啟動了");
		    }
		}


ServletContextAttributeListener 

	用於監聽ServletContext中屬性的變化 

	案例:
		@WebListener
		public class MyServletContextAttributeListener implements ServletContextAttributeListener {
		
			/**
			 * 當向上下文中 新增屬性時, 程式碼執行
		     */
		    public void attributeAdded(ServletContextAttributeEvent e)  { 
		    	//從事件物件中, 得到儲存的鍵和值
		    	String key = e.getName();
		    	Object value = e.getValue();
		    	System.out.println("監聽到ServletContext中資料的增加:"+key+" --> "+value);
		    }
		
			/**
			 * 當從上下文中 移除屬性時, 程式碼執行
		     */
		    public void attributeRemoved(ServletContextAttributeEvent e)  { 
		    	//從事件物件中, 得到被移除的鍵和值
		    	String key = e.getName();
		    	Object value = e.getValue();
		    	System.out.println("監聽到ServletContext中資料的移除:"+key+" --> "+value);
		    }
		
			/**
			 * 當操作上下文物件 儲存資料 ,發生替換時, 程式碼執行.
		     */
		    public void attributeReplaced(ServletContextAttributeEvent e)  { 
		    	//從事件物件中, 得到被替換掉的舊的鍵和值
		    	String key = e.getName();
		    	Object oldValue = e.getValue();
		    	System.out.println("監聽到ServletContext中資料的替換, 舊資料:"+key+" --> "+oldValue);
		    	//從事件物件中, 得到上下文物件
		    	Object newValue = e.getServletContext().getAttribute(key);
		    	System.out.println("監聽到ServletContext中資料的替換, 新資料:"+key+" --> "+newValue);
		    }
			
		}


HttpSessionListener 

	用於監聽session的建立與銷燬. 

	@WebListener
	public class MySessionListener implements HttpSessionListener {
		
		private static int count = 0;
		private static Random r = new Random();
		ArrayList<Integer> nums = new ArrayList<>();
	    public void sessionCreated(HttpSessionEvent arg0)  { 
	    	//30-60的隨機數字
	    	int num = r.nextInt(31)+30;
	    	count+=num;
	    	nums.add(num);
	    }
	
	    public void sessionDestroyed(HttpSessionEvent arg0)  { 
	    	int num = nums.remove(nums.size()-1);
	    	count-=num;
	    }
	    
	    public static int getCount() {
	    	return count;
	    }
		
	}

HttpSessionAttributeListener

用於監聽session中的資料的增加 ,刪除, 修改.

	public class LoginServlet2 extends HttpServlet {

		/**
		 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
		 */
		protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
			String uname = request.getParameter("uname");
			String upass = request.getParameter("upass");
			
			request.getSession().setAttribute("username", request.getRemoteAddr()+":"+uname);
			response.sendRedirect("home2.jsp");
		}
	
		/**
		 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
		 */
		protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
			// TODO Auto-generated method stub
			doGet(request, response);
		}
	
	}