1. 程式人生 > >Servlet容器模型

Servlet容器模型

一、ServletContext介面

javax.servlet.ServletContext

1、得到servletcontext引用

//方法一:直接呼叫getServletContext()

ServletContext context=getServletContext();

//方法二:先得到ServletContext引用後,在呼叫它的getServletContext()

ServletContext context=getServletConfig().getServletContext();

2、獲取應用程式的初始化引數

檢索Servlet上下文初始化引數的兩個方法:

public String getInitParameter(String name);//返回指定引數名的字串引數值,如果引數不存在返回null

public Enumeration getInitParameterNames();//返回一個包含所有初始化引數名稱的Enumeration物件

3、通過ServletContext物件獲得資源

使用這些方法可以訪問任何資源而不必關心資源所處的位置

public URL getResource(String path);//返回由給定路徑指定的資源的URL物件。路徑必須以“/”開頭,相對於Web應用程式的文件根目錄

public InputStream getResourceAsStream(String path);

//等價於getResource(path).openStream()從資源獲得一個InputStream物件

public String getRealPath(String path);//返回給定的相對路徑的絕對路徑

例子:

package com.dom;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/filedownload")
public class fileDownloadServlet extends HttpServlet{
	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		//設定檔案的內容型別
		response.setContentType("image/jpg");
		//設定content-Disposition響應頭,指定檔名
		response.setHeader("Content-Disposition","attachment;filename=duke.jpg");
		//獲得輸出流物件
		OutputStream os=response.getOutputStream();
		ServletContext context=getServletContext();
		//返回輸入流物件
		InputStream is=context.getResourceAsStream("/imgs/img3.jpg");
		byte[] bytearray=new byte[1024];
		int bytesread=0;
		//從輸入流中讀取1KB,然後寫到輸出流中
		while((bytesread=is.read(bytearray))!=-1){
			//將資料傳送到客戶端
			os.write(bytearray, 0, bytesread);
		}
		os.flush();
		is.close();
	}
	

}

圖片文件位置

執行結果:

4、登入日誌

使用ServletContext介面的log()可以將指定的資訊寫到伺服器的日誌檔案中,該方法有兩種格式:

public void log(String msg);//引數msg為寫到日誌檔案中的訊息

public void log(String msg,Throwable throwable);//將msg指定的訊息和異常的棧跟蹤資訊寫入日誌檔案

5、使用RequestDispatcher實現請求轉發

RequestDispatcher getRequestDispatcher(String path);//引數path表示資源路徑,必須以“/”開頭,表示相對於Web應用的文件根目錄。如果不能返回一個RequestDispatcher物件,該方法將返回null

RequestDispatcher getNamedDispatcher(String name);//引數name為一個命名的Servlet物件。Servlet和JSP頁面都可以通過DD檔案指定名稱

6、使用ServletContext物件儲存資料

使用ServletContext物件也可以儲存資料,該物件也是一個作用域物件,作用域是整個應用程式。

處理屬性的方法

public void setAttribute(String name,Object object);//將給定名稱的屬性值物件繫結到上下文物件上。

public Object getAttribute(String name);//返回繫結到上下文物件上的給定名稱的屬性值,如果沒有該屬性,返回null

public Enumeration getAttributeNames();//返回繫結到上下文物件上的所有屬性名的Enumeration物件

public void removeAttribute(String name);//從上下文物件中刪除指定名稱的屬性

7、檢索Servlet容器的資訊

getServerInfo()//返回Servlet所執行容器的名稱和版本

getMajorVersion()//返回容器所支援的Servlet API的主版本號

getMinorVersion()//返回容器所支援的Servlet API的次版本號

getServletContextName()//返回與該ServletContext對應的Web應用程式名稱,它是在web.xml中使用<display-name>元素定義的名稱

二、會話管理

1、理解狀態與會話

協議記住使用者及其請求的能力稱為狀態(state)。按這個觀點,協議分成兩種型別:有狀態的和無狀態的

HTTP的無狀態特性

HTTP是一種無狀態的協議,HTTP伺服器對客戶的每個請求和響應都是作為一個分離的事務處理。伺服器無法確定多個請求是來自相同的客戶還是不同的客戶,這意味著伺服器不能在多個請求中維護客戶的狀態。

會話的概念

會話(session)是一個客戶與伺服器之間的不間斷的請求響應序列。當一個客戶向伺服器傳送第一個請求時就開始了一個會話。對該客戶之後的每個請求,伺服器能夠識別出請求來自同一個客戶。當客戶明確結束會話或伺服器在一個預定義的時限內沒從客戶接受任何請求時,會話就結束了。當會話結束後,伺服器就忘記了客戶以及客戶的請求。

2、會話管理機制

容器通過javax.servlet.http.HttpSession介面抽象會話的概念。

容器使用HttpSession物件管理會話的過程

1、當客戶向伺服器傳送第一個請求時,伺服器就可以為該客戶建立一個HttpSession會話物件,並將請求物件與該會話物件關聯。伺服器在建立會話物件時為其指定一個唯一識別符號,稱為會話ID,它可作為該客戶的唯一標識。此時,該會話處於新建狀態,可以使用HttpSession介面的isNew()來確定會話是否屬於該狀態。

2、當伺服器向客戶傳送響應時,伺服器將該會話ID與響應資料一起傳送給客戶,這是通過Set-Cookie響應頭實現的,響應訊息例如:

HTTP/1.1/ 200 OK

Set-Cookie:JSESSIONID=2387378C687AFD8C892AC845ACF678A3

Content-Type:text/html

..............

這裡JSESSIONID的值即為會話ID,它是32位的十六進位制數

3、客戶在接收到響應後將會話ID儲存在瀏覽器的記憶體中。當客戶再次向伺服器傳送一個請求時,它將通過Cookie請求頭把會話ID與請求一起傳送給伺服器。例如

POST/helloweb/selectProduct.do HTTP/1.1

Host:www.mydomain.com

Cookie:JSESSIONID=2387378C687AFD8C892AC845ACF678A3

..................

4、伺服器接受到請求後,從請求物件中取出會話ID,在伺服器中查詢之前建立的會話物件,找到後將該請求與之前建立的ID值相同的會話物件關聯起來。

上述過程的第(2)~(4)步一直保持重複。如果客戶在指定時間沒有傳送任何請求,伺服器將使會話物件失效。一旦會話物件失效,即使客戶的請求被認為是第一次請求(如第一步),它不與某個存在的會話物件關聯。伺服器可以為客戶建立一個新的會話物件。

*不能使用客戶的IP地址唯一標識客戶。因為,客戶可能是通過區域網訪問Internet.儘管在區域網中每個客戶有一個IP地址,但對於伺服器來說,客戶的實際IP地址是路由器的IP地址,所以該區域網的所有客戶的IP地址都相同,因此也就無法唯一標識客戶。

3、HttpSession API

1、public String getId()//返回為該會話指定的唯一識別符號,它是一個32位的十六進位制數

2、public long getCreationTime()//返回會話建立的時間。時間為從1970年1月1日午夜到現在的毫秒數

3、public long getLastAccessedTime()//返回會話最後被訪問的時間

4、public boolean isNew()//如果會話物件還沒有同客戶關聯,則返回true

5、public ServletContext getServletContext();//返回該會話所屬的ServletContext物件

6、public void setAttribute(String name,Object value)//將一個指定名稱和值的屬性儲存到會話物件上

7、public Object getAttribute(String name)//返回儲存到會話上的指定名稱的屬性值。如果沒有指定名稱的屬性,則返回null

8、public Enumeration getAttributeNames()//返回儲存在會話上的所有屬性名的一個列舉物件

9、public void removeAttribute(String name)//從會話中刪除儲存的指定名稱的值

10、public void setMaxInactiveInterval(int interval)//設定在容器使該會話失效前客戶的兩個請求之間最大間隔的時間,單位為秒。引數為負值表示會話永不失效

11、public int getMaxInactiveInterval()//返回以秒為單位的最大間隔時間,在這段時間內,容器將在客戶請求之間保持該會話開啟狀態

12、public void invalidate()//使會話物件失效並刪除儲存在其上的任何物件

4、使用HttpSession物件

使用HttpSession物件通常需要三步:

1、建立或返回與客戶請求關聯的會話物件

2、在會話物件中新增或刪除“名/值”物件

3、如果需要可使會話失效

建立或返回HttpSession物件需要使用HttpServletRequest介面提供的getSession()

public HttpSession getSession(boolean create)//返回或建立與當前請求關聯的會話物件。如果沒有與當前請求關聯的會話物件,當引數為true時建立一個新的會話物件,當引數為false時,返回null

public HttpSession getSession()//該方法與呼叫getSession(true)等價

例子

package com.session;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

@WebServlet("/ShowSessionServlet.do")
public class ShowSessionServlet extends HttpServlet {

	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		response.setContentType("text/html;charset=utf-8");
		//建立或返回使用者會話物件
		HttpSession session=request.getSession(true);
		String heading=null;
		//從會話物件中檢索accessCount屬性
		Integer accessCount=(Integer)session.getAttribute("accessCount");
		if(accessCount==null){
			accessCount=new Integer(1);
			heading="歡迎您,首次登陸該頁面";
		}else{
			heading="歡迎您,再次登陸該頁面";
			accessCount+=1;
		}
		//將accessCount作為屬性儲存到會話物件中
		session.setAttribute("accessCount",accessCount);
		PrintWriter out=response.getWriter();
		out.println("<html><head>");
		out.println("<title>會話跟蹤例項</title></head>");
		out.println("<body><center>");
		out.println("<h4>"+heading+"<a href='ShowSessionServlet.do'>再次訪問</a></h4>");
		out.println("<table border='0'>");
		out.println("<tr bgcolor='#ccc'><td>資訊</td><td>值</td><tr/>");
		String state=session.isNew()?"新會話":"舊會話";
		out.println("<tr><td>會話狀態</td><td>"+state+"</td></tr>");
		out.println("<tr><td>會話ID</td><td>"+session.getId()+"</td></tr>");
		out.println("<tr><td>建立時間</td><td>"+new Date(session.getCreationTime())+"</td></tr>");
		out.println("<tr><td>最近訪問時間</td><td>"+new Date(session.getLastAccessedTime())+"</td></tr>");
		out.println("<tr><td>最大不活動時間</td><td>"+session.getMaxInactiveInterval()+"</td></tr>");
		out.println("<tr><td>Cookie</td><td>"+request.getHeader("Cookie")+"</td></tr>");
		out.println("<tr><td>Set-Cookie</td><td>"+request.getHeader("Set-Cookie")+"</td></tr>");
		out.println("<tr><td>已被訪問的次數</td><td>"+accessCount+"</td></tr>");
		out.println("</table></center></body></html>");
	}
}

執行結果:

5、會話超時與失效

可以在DD檔案中設定會話超時時間

<session-config>
    <session-timeout>10</session-timeout>
</session-config>

<session-timeout>元素中指定以分鐘為單位的超時期限,預設下30分鐘。0或小於0的值表示會話永不過期。如果使用者在指定期限內沒有執行任何動作,伺服器就認為使用者處於不活動狀態並使會話物件失效。

例子:

package com.session.timeout;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/*
 * 當使用GET請求訪問它時,生成一個0~100之間的隨機整數,將其作為一個屬性儲存到使用者的會話物件中,同時提供一個表單供使用者輸入猜測的數。
 * 如果該Servlet接收到一個POST請求,它將比較使用者猜到的數和隨機生成的數是否相等。若相等在響應頁面中給出資訊,否則,應該告訴使用者猜的數
 * 是大還是小,並允許使用者重新猜
 */
@WebServlet("/GuessNumberServlet")
public class GuessNumberServlet extends HttpServlet {
	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
	int magic=(int)(Math.random()*101);
	HttpSession session=request.getSession();
	//session.setAttribute(String,Object);
	session.setAttribute("num", new Integer(magic));
	response.setContentType("text/html;charset=utf-8");
	PrintWriter out=response.getWriter();
	out.println("<html><head>");
	out.println("<title>猜數字</title>");
	out.println("</head><body>");
	out.println("我想出一個0到100之間的數,請你猜!");
	out.println("<form action='GuessNumberServlet' method='post'>");
	out.println("<input type='text' style='width:300px' name='guess'>");
	out.println("<input type='submit' value='確定'/>");
	out.println("</form></body></html>");
	}
	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		int guess=Integer.parseInt(request.getParameter("guess"));
		HttpSession session=request.getSession();
		int magic=(Integer)session.getAttribute("num");
		response.setContentType("text/html;charset=UTF-8");
		PrintWriter out=response.getWriter();
		out.println("<html><body>");
		if(guess==magic){
			session.invalidate();//銷燬會話物件
			out.println("祝賀你,答對了");
			out.println("<a href='/ServletContext/GuessNumberServlet'>再來一次</a>");
		}
		else{
			if(guess>magic){
				out.println("太大了,請重猜!");
			}else{
				out.println("太小了,請重猜!");
			}
			out.println("<form action='GuessNumberServlet' method='post'>");
			out.println("<input type='text' name='guess' style='width:300px'/>");
			out.println("<input type='submit' value='確定'/>");
			out.println("</body></html>");
		}
	}
	

}

二、Cookie及其應用

1、Cookie API

javax.servlet.http.Cookie

public Cookie(String name,String value)

Cookie類的常用方法

1、public String getName()//返回Cookie名稱,名稱一旦建立不能改變

2、public String getValue()//返回Cookie的值

3、public void setValue(String new Value)//在Cookie建立後為它指定一個新值

4、public void setMaxAge(int expiry)//設定Cookie在瀏覽器中的最長存活時間,單位秒。負值表示不永久儲存,0表示刪除該Cookie

5、public void getMaxAge()//返回Cookie在瀏覽器上的最大存活時間

6、public void setDomain(String pattern)//設定該Cookie所在的域。域名以點號(.)開頭,例如:.foo.com。預設情況下,只有傳送Cookie的伺服器才能得到它。

7、public String getDomain()//返回為該Cookie設定的域名

2、向客戶端傳送Cookie

    要把Cookie傳送到客戶端,Servlet先使用Cookie類的構造方法建立一個Cookie物件,通過setXxx()設定各種屬性,通過響應物件的addCookie(cookie)把Cookie加入響應頭。

   1、建立Cookie物件

Cookie  userCookie=new Cookie("username","hacker");

   2、設定Cookie的最大存活時間

userCookie.setMaxAge(60*60);//一小時

在預設情況下,傳送到客戶端的Cookie物件只是一個會話級別的Cookie,它儲存在瀏覽器的記憶體中,使用者關閉瀏覽器後Cookie物件將被刪除。如果希望瀏覽器將Cookie物件儲存磁碟上,需要使用Cookie類的setMaxAge()設定Cookie的最大存活時間。

   3、向客戶傳送Cookie物件

要將Cookie物件傳送到客戶端,需要呼叫響應物件的addCookie()將Cookie新增到Set-Cookie響應頭

response.addCookie(userCookie);

3、從客戶端讀取Cookie

需從客戶端讀入Cookie,Servlet應該呼叫請求物件的getCookies(),該方法返回一個Cookie物件的陣列。大多數情況下,只需迴圈訪問陣列的各個元素尋找指定名字的Cookie,然後對該Cookie呼叫getValue()取得與指定名字關聯的值。

步驟:

1、呼叫請求物件的getCookies方法

該方法返回一個Cookie物件的陣列。如果請求中不含Cookie,返回null

Cookie[] cookies=request.getCookies();

2、對Cookie陣列迴圈

4、Cookie的安全問題

瀏覽器 一般只允許存放300個Cookie,每個站點的Cookie最多存放20個,每個Cookie的大小限制為4KB,因此Cookie不會佔據硬碟太多空間。

5、例子

自動登入功能

以GET方法訪問CheckUserServlet(在瀏覽器位址列中輸入該Servlet的URL地址),由於是首次訪問,請求中並不包含Cookie,該Servlet將響應重定向到MyJsp.jsp頁面。在該頁面中如果使用者輸入了正確的使用者名稱和口令,並勾選了“自動登入”複選框,單擊“提交”按鈕,將傳送POST請求由CheckUserServlet的doPost()處理。在該方法中使用使用者名稱和口令建立兩個Cookie物件併發送到客戶端。

之後在傳送GET請求,Servlet將從Cookie中檢索出使用者名稱和口令,並對其驗證。驗證通過後將響應重定向到Welcome.jsp頁面

CheckUserServlet.java

package com.MyCookieTest;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/checkUserServlet")
public class CheckUserServlet extends HttpServlet {
	String message=null;
	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		response.setContentType("text/html;charset=utf-8");
		System.out.println("1");
		String value1="value1";
		String value2="value2";
		Cookie cookie=null;
		Cookie[]cookies=request.getCookies();
		if(cookies!=null){
			for(int i=0;i<cookies.length;i++){
				cookie=cookies[i];
				if(cookie.getName().equals("username")){
					value1=cookie.getValue();
				}
				if(cookie.getName().equals("password")){
					value2=cookie.getValue();
				}
			}
				
				if(value1.equals("admin")&&value2.equals("admin")){
					message="歡迎您"+value1+"再次登入該頁面!";
					request.getSession().setAttribute("message",message);
					response.sendRedirect("Welcome.jsp");
				}else{
					response.sendRedirect("MyJsp.jsp");
				}
				System.out.println(request.getHeader("Cookie"));
			}
		
		
	}
	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		System.out.println("2");
		message=null;
		response.setContentType("text/html;charset=utf-8");
		String username=request.getParameter("username");
		String password=request.getParameter("password");
		if(!username.equals("admin")){
			if(!password.equals("admin")){
				message="使用者名稱與口令不匹配,請重試";
				request.getSession().setAttribute("message", message);
				response.sendRedirect("http://localhost:8080/MyCookie/MyJsp.jsp");
			}
		}else{
			//如果使用者選中了“自動登入”複選框,向瀏覽器傳送兩個Cookie
			if(request.getParameter("check")!=null&&request.getParameter("check").equals("check")){
				Cookie nameCookie=new Cookie("username",username);
				Cookie passCookie=new Cookie("password",password);
				nameCookie.setMaxAge(60*60);
				passCookie.setMaxAge(60*60);
				response.addCookie(nameCookie);
				response.addCookie(passCookie);
			}
			message=username+"已經成功登入";
			request.getSession().setAttribute("message",message);
			System.out.println(request.getHeader("Cookie"));
			response.sendRedirect("http://localhost:8080/MyCookie/Welcome.jsp");
			
		}
	}
	

}

MyJsp.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>登入頁面</title>
  </head>
  <body>
    	${sessionScope.message}<br>
    	<form action="checkUserServlet" method="post">
    		請輸入使用者名稱和口令:<br>
    		使用者名稱:<input type="text" name="username"/><br/>
    		口令:<input type="password" name="password"/><br/>
    		<input type="checkbox" name="check" value="check"/>自動登入<br>
    		<input type="submit" value="提交"/>
    		<input type="reset" value="重置"/>
    	</form>
  </body>
</html>

Welcome.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
  
    
    <title>歡迎頁面</title>
    
  </head>
  
  <body>
    This is my JSP page. 
  </body>
</html>

測試結果: