Servlet
一 概述
1.伺服器
處理請求、給出響應的全部因素構成的整體稱作伺服器,包含硬體與軟體兩個方面。
2.Servlet規範
伺服器處理請求、響應遵守的原則。
3.Servlet是什麼?
Server Applet,運作在伺服器端的java應用程式,使用java語言編寫,遵守java規範,Servlet規範的核心。
4.Servlet在Web伺服器中的地位
Servlet在整個Web伺服器中充當控制器的角色,将請求轉發給相應的業務邏輯處理。
5.tomcat是什麼?
tomcat是伺服器的軟體方面,是一個實作了Servlet規範與JSP規範的容器,由Apache提供、免費、開放源代碼的輕量級應用伺服器,适用于中小型系統和并發通路使用者不是很多的場合。
6.MIME
Multipurpose Internet Mail Extensions,多用途網際網路郵件擴充協定,為每一種類型的檔案制定一種規範以便浏覽器接收到檔案以後調用相應的元件打開。
二 幾個重要類
1.ServletContext
- ServletContext,Servlet上下文,是所用Servlet存在的環境,一個Web應用程式,一個執行個體對象,在整個應用層級起作用。
- JSP内置對象application就是一個ServletContext執行個體,
- ServletContext在檔案上對應于web.xml,web.xml包含整個伺服器中使用者自定義的全部資訊,從此可以看出ServletContext的作用範圍。
- 作用:ServletContext主要用于在Web伺服器啟動時加載各元件到伺服器,根據自定義參數初始化伺服器。
- 生命周期:ServletContext對象在伺服器啟動時建立,伺服器停止時銷毀。
重要方法:
servletContext.setAttribute(name, object);//向servletContext作用域中添加屬性,該屬性為所有使用者共享
servletContext.getAttribute(name);//擷取servletContext作用域中指定屬性的屬性值
servletContext.removeAttribute(name);//從servletContext作用域中删除指定屬性
servletContext.getRealPath(path);//根據相對于項目的路徑擷取資源的絕對路徑URL
servletContext.getResourceAsStream(path);//擷取路徑檔案的輸入流
servletContext.getInitParameter(name);//擷取初始化參數的值
對象擷取:
ServletContext對象由Web伺服器在啟動時自動建立,程式員需要做的就是擷取該執行個體對象,以下是幾種擷取方式。
this.getServletContext();//HttpServlet提供了擷取ServletContext執行個體對象的方法,在doGet或者doPost方法内部
request.getServletContext();//通過request對象擷取
HttpSession session = request.getSession();
session.getServletContext();//通過session對象擷取
2.ServletConfig
用于初始化Servlet,一個Servlet,一個ServletConfig。
public void init(ServletConfig config) throws ServletException {
String initParameter = config.getInitParameter("initParamName");
}
三 Servlet
1.Servlet的工作原理:
Web容器啟動時會建立兩個與Servlet相關的Map集合,兩個集合的key值均為urlPattern,即請求uri,第一個Map的value是Servlet的引用變量,第二個Map的value是Servlet的全限定性類名。請求到達Web容器後,系統先搜尋第一個Map集合,如果存在與uri對應的引用變量,則擷取該引用變量,如果不存在,繼續搜尋第二個Map集合,擷取對應的全限定類型,建立對象,并把引用變量存到第一個Map集合中。
2.Servlet繼承結構
<interface> <interface> <interface>
Servlet ServletConfig Serializable
| | |
--------------------------------------------------
|
<abstract> GenericServlet
<abstract> HttpServlet
3.實際開發中Servlet的建立方式
Servlet接口是Servlet規範中定義的,伺服器自動調用其中service方法處理請求,該接口有多個抽象方法,很多在實際開發中很少使用,實作該接口需要實作其中的全部抽象方法,是以不采用直接實作Servlet接口的方法建立Servlet。
GenericServlet實作Servlet接口中大多數抽象方法,保留了一個抽象方法service,繼承該抽象類建立servlet,必須實作該抽象方法。HTTP中多個請求方法在處理請求前必須做一些固定的前置工作,如果實作該serivce方法就需要在每一個Servlet的service方法中都編寫前置工作代碼,造成代碼備援。為了解決此問題,tomcat提供了一個GenericServlet的子類HttpServlet,HttpServlet采用固定行為結構的模闆方法模式将前置工作固定在一個方法,使用者在建立Serlvet時繼承HttpServlet,然後重寫與請求方式對應的方法即可。(具體參考源碼)
HttpServlet源碼
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
} catch (IllegalArgumentException iae) {
// Invalid date header - proceed as if none was set
ifModifiedSince = -1;
}
if (ifModifiedSince < (lastModified / 1000 * 1000)) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
從中可以看出Get請求在實際執行前做了很多準備工作。
4.生命周期
Servlet預設在調用時建立并初始化,這也是工作原理中第一個Map集合引用變量為空情況出現的原因。對一些必定會被通路并且通路頻繁的Servlet可以設定為在容器啟動時建立并初始化,提高通路速度。
<load-on-startup>int類型數字</load-on-startup>
小于0:預設值,調用時建立初始化。
等于大于0:在Web伺服器啟動時建立初始化,值越小,優先級越高。出現相同值時,不會出現異常,容器自定義順序執行。
5.主要方法:
this.getInitParameter("");//擷取初始化參數
this.getServletName();//擷取配置檔案<servlet-name>标簽的内容
this.getServletContext();
6.由于繼承HttpServlet建立的Servlet屬于自定義類,系統不知曉,必須在配置檔案中配置:
<servlet>
<servlet-name>bothRegisterServlet</servlet-name>
<servlet-class>com.servlet.register.BothRegisterServlet</servlet-class>
<init-param>
<param-name>xxx</param-name>
<param-value>xxxx</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>bothRegisterServlet</servlet-name>
<url-pattern>/bothRegisterServlet01</url-pattern>
</servlet-mapping>
由于通路Web容器中的Servlet對象時,無論采用哪種方式都隻能間接通路,一般采用url通路servlet,那麼就需要在通路時使用的url與最終資源servlet之間建立一對一的映射關系,這就是servletMapping存在的原因。
允許為一個Servlet對象配置多個url格式。
四 request
1.什麼是request?
Servlet規範定義的一個向Servlet提供用戶端請求資訊的對象。
2.request中包含哪些資訊,或者通過request可以擷取哪些資訊?
- 請求參數:當表單輸入為空時,伺服器端擷取的内容為空字元串,不為null。
- request作用域内的屬性:可以将資料存放到request作用域中,在同一請求中擷取。
- 輸入流:檔案上傳時,可以通過request擷取上傳檔案的輸入流。
- Cookie:Cookie由伺服器生成,儲存在客戶單,浏覽器向伺服器送出請求時自動送出。
- Part:代表上傳檔案的對象。
- 其他内置對象:如session\application。
- 其他與請求密切相關的資訊:如請求方式、使用的浏覽器、協定、請求url、請求體長度、查詢字元串、請求頭、用戶端IP、端口号等。
3.常用方法:
⑴request.getParameter()與request.getAttribute()對比
- getParameter:擷取請求參數的值,傳回值String,表單輸入為空時,傳回值為空字元串,不為null。
- getAttribute:擷取作用域範圍内的屬性,傳回類型為屬性類型,屬性不存在時,傳回null。
⑵request.getSession()與request.getSession(boolean create)對比
- getSession():與getSession(true)相同,基于Cookie擷取Session對象,如果Cookie不存在,則建立session。
- getSession(false):基于Cookie擷取Session對象,如果Cookie不存在,則傳回null。
⑶request.setCharacterEncoding():設定請求體的編碼方式,即伺服器解析浏覽器送出的資料時采用的編碼方式。由于隻有POST請求的資料通過請求體傳輸,是以隻對POST請求有意義,對GET請求無意義。
⑷request.getContentLength():擷取請求體的位元組長度,隻對POST有意義。
⑸request.getQueryString():擷取追到URL後面的請求字元串,隻對GET請求有意義。
⑹request.getRequestDispatcher().forward(request,response)request.getRequestDispatcher().include(request,response)的差別:
持有響應權的資源不同:
- forward将請求繼續向前推進,将響應權轉交給後面的資源,自身無法向伺服器發出響應,即在自身中使用out.write方法不能向頁面輸出内容。
- include包含,将後面的資源作為自身的一部分,不僅後面的資源可以發出響應,自身也可以響應,相當于将自身的一部分代碼分到後面的資源中。
4.什麼是同一請求?
一次請求在結束前,如果未進行重定向操作,進行轉發與包含操作,仍然屬于同一請求的範疇。
5.同一請求的意義
同一請求共享請求參數與作用域中的屬性等資訊。
五 response
1.什麼是response?
Servlet規範定義的一個向浏覽器發出響應的對象,由Servlet容器自動建立。
2.reponse主要作用:
⑴設定響應的MIME類型及編碼方式。
由于存在多種響應資料類型,是以伺服器在響應前必須指明資料類型,以便浏覽器根據指定類型處理接收的資料。
response.setContentType("text/html;charset=UTF-8");//以HTML方式處理
⑵設定響應頭,即響應内容的其他方面:
response.setHeader(name,value);
⑶建立并将Cookie儲存到用戶端:
response.addCookie(Cookie cookie);
⑷重定向:
response.sendRedirect(url);
⑸擷取向浏覽器輸出内容的輸出流:
PrintWriter out=reponse.getWriter();
⑹擷取檔案下載下傳的輸出流:
ServletOutputStream sos=response.getOutputStream();
3.響應或者跳轉後的代碼執行問題
代碼繼續執行,對響應結果或者request作用域沒有影響,因為響應已經結束,請求已轉發給其他資源,自身就失去了處理請求的能力,但對session\applicatiion作用域可以産生影響。通常不在響應或者跳轉之後對整個處理過程施加影響。
六 Cookie
1.什麼是Cookie?
由伺服器生成,儲存在浏覽器端的存儲會話資訊的對象。
2.建立Cookie
Servlet規範提供了Cookie類,用于建立Cookie對象:
Cookie cookie=new Cookie(name,value);
3.儲存Cookie
由伺服器建立,儲存在用戶端,浏覽必須允許儲存Cookie将cookie:
response.addCookie(cookie);
4.設定Cookie路徑:
Cookie在通路伺服器時自動送出,這種送出不是無選擇地對任何通路路徑都送出,而是隻對設定的路徑送出。在未單獨設定Cookie的綁定路徑時,Cookie與生成Cookie的通路路徑綁定,通路該路徑下任何一個資源時,浏覽器自動送出Cookie。
可以設定Cookie的綁定路徑,使Cookie不受生成路徑的限制:
cookie.setPath(String uri);
5.Cookie的生命周期
預設情況下,Cookie儲存在浏覽器緩存中,浏覽器關閉,Cookie銷毀。如果希望Cookie中儲存的資訊長期存在,可以将Cookie儲存到本地硬碟中,前提是目前浏覽器支援。
cookie.setMaxAge(int expiry);//以秒為機關
- 取值大于0:将Cookie儲存到硬碟中,無論浏覽器是否關閉,指定時長過去後,Cookie被銷毀。
- 等于0:立即銷毀Cookie。
- 小于0:預設情況,将Cookie儲存在浏覽器緩存中,浏覽器關閉,Cookie銷毀。
6.常用方法:
String name = cookie.getName();//擷取Cookie的名稱
String value = cookie.getValue();//擷取Cookie的值
cookie.setValue(newValue);//修改Cookie的值
7.主要應用:
Cookie主要用于儲存浏覽器關閉以後需要保留的會話資訊,如登陸資訊,實作免登陸功能。
七 Session
1.HTTP協定的無狀态特性
在HTTP協定中,浏覽器向伺服器發送請求,伺服器響應完畢後,連接配接結束,伺服器端沒有儲存本次請求的任何資訊,這就是HTTP協定的無狀态特性。如果需要儲存會話資訊,就必須提供一種解決方案,而Session就是這個解決方案。
2.什麼是Session?
Session是用來在伺服器端儲存會話資訊的對象,比如儲存使用者在網站上的足迹等。
3.什麼是同一Session?
在浏覽器開啟Cookie的情況下,從浏覽器第一個通路網站到浏覽器關閉的時間内浏覽器與伺服器所有的互動都是在同一個會話中。如果浏覽器關閉了Cookie,那麼浏覽器每向伺服器發送一次請求,都開啟一個新的會話,即伺服器端都會建立一個Session對象。
4.Session的工作原理
第一次通路時,伺服器會自動建立一個Session對象,并為Session對象配置設定一個唯一的32位的id,同時生成一個Cookie對象,name為JSESSIONID,value為Session的id。在Cookie的有效期内,再次通路伺服器時,根據value值擷取對應的Session對象,這樣就保證了在同一次會話中存在的始終是同一個Session對象。
5.Session的生命周期
Session對象在浏覽器第一次通路伺服器時建立,如果浏覽器長時間不向伺服器發送請求,在指定的時長之後,伺服器會銷毀Session對象。
這裡所說的時長不是從Session建立到銷毀的時間長度,而是浏覽器長時間發送請求,伺服器儲存Session對象的最大時間長度,通過以下方法設定,以秒為機關:
session.setMaxInactiveInterval(int interval);
6.Session的銷毀
通過以下方法,浏覽器可以主動銷毀Session對象:
session.invalidate();
7.主要方法:
Session主要用于在同一會話中共享資料,是以對Session的主要操作是操作作用域中的屬性:
session.setAttribute(name,value);//向作用域中添加屬性
session.getAttribute(name);//擷取作用域中的屬性
session.removeAttribute(name);//從作用域中删除屬性
八 請求轉發與重定向
1.什麼是請求轉發?
伺服器接收到請求以後,首先對請求進行預處理,然後将請求轉發給其他的資源繼續處理,這種轉發叫做請求轉發。
2.什麼是重定向?
伺服器調用特定的方法向浏覽器發送一個資源路徑,浏覽器通路該路徑,這一過程叫做重定向。
3.請求轉發與重定向的差別
- 請求轉發是伺服器調用不同的資源處理同一請求,始終是同一請求。
- 重定向使得浏覽器再次向伺服器發送請求,前後是兩個不同的請求。
九 異步機制
1.什麼是異步機制?
為耗時的任務配置設定一個線程,主線程繼續執行後面的代碼,執行完畢,将主線程歸還線程池,以便執行其他的請求。
2.異步機制産生的原因
Servlet是單例多線程的,允許并發通路的線程數目有限,為此Servlet建立了一個線程池,請求必須從線程池中擷取了線程才能通路Servlet。若一個請求長時間占有線程,可能導緻後面的請求長時間等待,降低了程式的吞吐能力。如果一個線程從Servlet線程池中擷取了線程以後,另外開啟一個線程處理耗時的任務,及時将主線程歸還線程池,就解決這個問題。
3.異步機制的作用
異步機制的作用主要不是為了提高單次執行速度,而是提高吞吐量,即同一時間段内允許更多的請求通路Servlet。為了提高通路的線程數目,降低每次通路占有Servlet線程的時間,将耗時的任何交個另外一個線程處理,将主線程及時歸還線程池。
4.異步機制的基本原理
Servlet接收到請求以後,對請求進行初步處理,然後開啟一個異步線程處理具體的業務,Servlet線程繼續執行後面的代碼,執行完畢後,将Servlet線程歸還線程池,以便其他請求使用。等待異步線程執行完畢後一起響應,異步線程執行完畢主要有兩個标志:
- 在異步線程内部調用complete方法。
- 逾時時間結束。
逾時時間不并代表異步線程的生命時長,而是最大生命時長。如果在逾時時長内,異步線程調用了complete方法,異步線程提前結束。
異步線程預設的逾時時長是10s(Servlet不同版本不同),當主線程執行完畢,同時逾時時長結束或者異步線程提前結束,伺服器開始向浏覽器發送響應,銷毀request、response對象,關閉輸出流,如果異步線程未執行完畢,那麼異步線程中已執行的響應會響應到浏覽器,未執行的響應不會響應到浏覽器。
5.響應時機
配置設定給異步線程的時間不是無限的,是以存在一個響應時機的問題。
在主線程執行完畢并且異步線程逾時時長用完或者提前結束時響應。
6.異步處理的實作
AsyncContext ac=request.startAsync();
ac.setTimeout(int mills);
ac.start(Runnable run);//将異步線程管理對象ac作為參數傳入異步線程中,通過該參數可以擷取request\response
7.異步機制的使用
由于異步機制的設計目的是為了使請求盡快歸還Servlet線程,提高程式的吞吐量,并未顯著提高響應速度。異步機制通常不直接用作向浏覽器輸出響應内容,如在異步線程内部使用out.write直接向浏覽器輸出,而是用來處理耗時的任務,将處理結果存放到session/application等作用域中。
8.還可以使用AsyncContext對象為異步線程添加監聽器,監聽異步線程的執行過程。
十 web.xml
1.配置Servlet、Filter、Listener、contextParams等構成應用程式的重要資訊。
2.配置歡迎頁面,也是網站的首頁,可以由多個檔案構成,優先采用上面的檔案:
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>default.html</welcome-file>
</welcome-file-list>
注:在配置檔案中書寫路徑時,隻有歡迎清單中的路徑不需要在前面加“/”,其他地方的路徑都需要在前面加“/”,因為歡迎頁面隻能放在WebContent根路徑下,不能放在WebContent根路徑下的檔案夾内,其路徑相對固定,是以可以簡寫。
3.配置錯誤頁面:
⑴方式一,根據錯誤代碼配置頁面,出現指定的錯誤代碼時顯示指定的頁面:
<error-page>
<error-code>404</error-code>
<location>/xxxx</location>//可以放在WebContent目錄下任意位置
</error-page>
⑵方式一,根據異常類型配置頁面,出現指定的異常時顯示指定的頁面:
<error-page>
<exception-type>java.lang.NullException</exception>//異常類必須寫完整的類名
<location>/xxxx</location>
</error-page>
4.配置ServletContext的初始化參數:
<context-param>
<param-name>paramName</param-name>
<param-value>paramValue</param-value>
</context-param>
十一 注解式開發
Servlet3.0新增注解式開發,注解式開發就是将在編寫在配置檔案web.xml中的資訊轉移到類檔案上。
注解方式:
在類名上添加注解:
@WebServlet(urlPatterns={"",""},loadOnStartup=1,initParams={@WebInitParam(name="",value="")},asyncSupported=true)
兩種注冊方式同時存在,如果映射路徑不相同,相當于存在一個集中了所用路徑的Servlet,如果存在相同路徑,伺服器無法啟動。
十二 檔案上傳
1.Servlet3.0提供一個Part類型,該類封裝了上傳檔案資訊。
2.檔案名存儲在一個名為Content-Disposition的請求頭中。
3.檔案上傳的具體實作:
Part part=request.getPart(檔案字段名);//擷取檔案封裝對象
String fileName=part.getHeader("Content-Dispostion");//擷取檔案名
part.write("parentPath/"+fileName);//将檔案儲存到指定路徑,隻能使用絕路徑
十三 檔案下載下傳
1.Servlet提供了ServletOutputStream用作檔案下載下傳的輸出流。
2.檔案下載下傳時必須設定浏覽器以附件的形式處理從伺服器擷取的資料:
response.setHeader("Content-disposition","attachment;filename=xxxx");
如果請求頭中設定的檔案名含有中文必須轉化為ISO-8859-1的編碼方式。
3.從伺服器擷取輸入流,利用ServletOutputStream輸出流将檔案寫到使用者指定路徑:
InputStream is = getServletContext().getResourceAsStream("/Files/upload.txt");
ServletOutputStream os = response.getOutputStream();
有了輸入流與輸出流,後續操作就是将輸入流中的内容寫入輸出流,這是IO常見的操作。
十四 線程安全問題
1.Servlet線程安全問題産生的原因
Servlet以單例的形式運作在多線程的環境中,其中的執行個體變量存儲在堆中的對象中,而堆是多線程共享的,是以執行個體變量存線上程安全問題,而靜态變量存儲在方法區,方法區也是多線程共享的,靜态變量也存線上程安全問題,而局部變臉存儲在棧中,棧中的資料在内部是共享的,在棧間是不共享的,即一個線程一個棧,是以局部變量時線程安全的。
2.線程安全問題的解決方案:
- 将全局變量轉化為局部變量。
- 将修改全局變量的代碼添加到同步方法或者同步塊中。
- 将全局變量存儲到ThrealLocal中,為每一個線程配置設定一個變量的副本,各個線程互相獨立操作。
十五 免登入
1.根據公開與保護程度,将網站上的資源分為開放性資源、權限性資源兩種:
- 開放性資源:對所用使用者公開,無需權限就可以通路的資源。
- 權限性資源:儲存使用者個人資訊,通過身份驗證後才可以通路的資源。
2.免登入的含義
登入網站以後,下次使用同一浏覽器通路網站,不需要重新登入。
3.免登入的實作原理
将登入資訊(使用者名、密碼)儲存在Cookie中,将Cookie儲存在本地,通路網站時浏覽器自動送出Cookie,經Filter或者Interceptor驗證通過後,無需登入直接通路。
4.免登入的條件
同一個浏覽器:免登入需要從本地擷取Cookie對象驗證資訊。
十六 Servlet的元件可插性
Servlet3.0元件具有可查性,指的是Servlet、Filter、Listener可以作為架包插入項目中。
架包建立:
- 建立項目Web Fragment Project。
- 打包jar File。
- 放入lib目錄下,作為架包使用。
使用:
十七 動态注冊