https://developer.ibm.com/zh/tutorials/j-javaee8-servlet4/
ServletRequest和HttpServletRequest區別
Servlet API是Java開發人員最熟悉的API之一,Servlet在1999年所發布的J2SE1.2版本中首次面世,Servlet在JavaWEB的開發中發揮着重要的作用。JavaEE8對Servlet4.0進行了重要的更新。其中服務器推送是最主要的更新,如果要使用服務器推送的功能,則我們必須使用HTTP/2.0版本的協議。JavaEE8提供了對Servlet映射的運行時發現,在運行時我們可以獲取Servlet的名稱,Servlet的映射路徑。JavaEE8簡化了對Filter的開發。
IDEA 2017.3版本才開始提供對JAVAEE8的支持,本文使用的是IDEA 2018.3版本,默認只支持Servlet4.0。
開發環境:jdk8,tomcat9,tomcat-native,openssl
Servlet4.0是使用HTTP/2協議,而Tomcat9下載后,默認使用的是HTTP/1.1,所以我們要先修改server.xml配置文件:
<!--
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
-->
2、開啟HTTP2的注釋,並刪除certificateChainFile這一行,HTTP2使用的端口不在是8080,而是8443,使用HTTP2需要配置一個私鑰文件和一個證書文件。
<Connector port="8443" protocol="org.apache.coyote.http11.Http11AprProtocol"
maxThreads="150" SSLEnabled="true" >
<UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol" />
<SSLHostConfig>
<Certificate certificateKeyFile="conf/localhost-rsa-key.pem"
certificateFile="conf/localhost-rsa-cert.pem"
certificateChainFile="conf/localhost-rsa-chain.pem"
type="RSA" />
</SSLHostConfig>
</Connector>
關於上面Certificate標簽有關說明:當你申請證書時或獲取一個證書crt和一個私鑰key文件,certificateKeyFile放的就是你的證書key文件,certificateFile則是私鑰cert文件,默認放在Tomcat9的conf目錄下。
certificateChainFile:一般操作系統/瀏覽器會內置一些CA(證書頒發機構)的證書,如果你的證書是直接由這些內置CA頒發的,那么就不需要Chain文件,瀏覽器可以直接識別你的證書。如果你的證書是由二級CA頒發的,即內置CA頒發給另一個二級CA,然后二級CA在頒發給你,那么就需要Chain文件,否則瀏覽器就不知道你的證書和內置CA的關系。如果你將自記得CA設置成了內置CA,那么直接由你的CA頒發的證書自然不需要Chain文件。
3、現在我們需要生成server.xml配置中的證書和私鑰文件,在這里我是使用openssl生成的,openssl windows版本網盤下載地址,提取碼:0sra ,網盤分享的openssl是win32版本的,由於windows64是兼容windows32的,所以不必擔心不能使用的問題。解壓開后,進入bin目錄,啟動openssl.exe
OpenSSL> genrsa -out localhost-rsa-key.pem 1024
OpenSSL> req -new -x509 -key localhost-rsa-key.pem -out localhost-rsa-cert.pem -days 3650 -config D:\soft\openssl\openssl-0.9.8k_WIN32\bin\openssl.cnf
4、將第三步生成了兩個文件,放入tomcat9的conf目錄下。
5、下載tomcat-nativate文件並解壓,如果你是win32系統直接將bin目錄下tcnative-1.dll和tcnative-1-src.pdb文件復制到jdk的bin目錄下,如果你是win64則將x64文件下的這兩個文件復制到jdk目錄下。
至此我們已經完成了Tomcat9的HTTP協議的升級,現在你可以啟動Tomcat9,然后訪問https://localhost:8443/即可看到tomcat頁面。
將用戶所需的WEB資源提前推送到用戶的瀏覽器緩存中,當用戶使用瀏覽器訪問所需WEB資源時,用戶不需要再次下載所需的資源,因為用戶所需的WEB資源已經存在與用戶的瀏覽器緩存中。
如下內容摘自這里:
Servlet4.0通過PushBuilder接口公開服務器推送。為了能夠進行訪問,你需要通過調用newPushBuilder()方法,從HttpServletRequest獲取PushBuilder實例。
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
PushBuilder pushBuilder = request.newPushBuilder();
}
每次調用newPushBuilder()方法時,都將返回PushBuilder的新實例。如果服務器推送不可用,newPushBuilder()將返回null。在某些情況下,客戶端可能會為請求事務拒絕服務器推送。如果客戶端沒有使用安全連接,服務器推送也不會起作用。因此,務必要在對PushBuilder實例調用方法前,針對null返回值進行測試。
顧名思義,PushBuilder實現Builder模式。在這一實現過程中,通過鏈接賦值方法構建推送請求。這些賦值方法通過設置請求HTTP標頭、方法類型(GET是唯一的可接受值)、查詢字符串、會話ID和資源路徑(即,將要推出的資源的路徑),來配置PushBuilder實例。
大多數來自原始HtpServletRequest實例的請求標頭,只添加到PushBuilder實例中。由於正確運行服務器推送並不需要某些標頭,因此不包括以下標頭:
設置推送資源路徑需要調用path()方法。該方法只能被調用一次,因為它會改變PushBuilder對象的路徑值。該路徑可能會以正斜杠(”/“)開頭,指示資源路徑是絕對路徑;否則,該資源會被認為是相對於關聯請求的上下文路徑。該路徑可以包含一個查詢字符串,該查詢字符串將與queryString()方法設置的任何字符串合並。
准備:使用一張圖片,復制一份並改名。如下代碼hello.jpg和hey.jpg使用的是同一張圖片,保證大小一致。清空瀏覽器緩存。
@WebServlet("/NoPushServlet")
public class NoPushServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.getRequestDispatcher("nopush.jsp").forward(req, resp);
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
Hello
<p><img src="./hello.jpg" alt=""></p>
</body>
</html>
不使用服務器推送,瀏覽器渲染hello.jsp頁面時會再次請求后台下載hello.jpg圖片,這會再次創建一個HTTP鏈接,發送請求耗時0.16ms,等待響應耗時6.49ms,最后下載圖片Content Download耗時1.13ms。
/** * 將用戶所需的WEB資源提前推送到用戶的瀏覽器緩存中,當用戶使用瀏覽器訪問所需WEB資源時, * 用戶不需要再次下載所需的資源,因為用戶所需的WEB資源已經存在與用戶的瀏覽器緩存中。 */ @WebServlet("/PushServlet") public class PushServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { PushBuilder pushBuilder = req.newPushBuilder(); if (pushBuilder != null) { PushBuilder path = pushBuilder.path("./hey.jpg"); path.push(); // 只推送最后一個path里的資源 // PushBuilder path = pushBuilder.path("./hey.jpg").path("./hello.jpg"); // path.push(); // 推送多個資源的寫法 // pushBuilder.path("./hey.jpg").push(); // pushBuilder.path("./hello.jpg").push(); } req.getRequestDispatcher("push.jsp").forward(req, resp); } }
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
Hello
<p><img src="./hey.jpg" alt=""></p>
</body>
</html>
使用服務器推送,效果很明顯,不需要向服務器二次請求建立連接,因為圖片在建立Servlet鏈接請求的時候,服務器端就已經推送過來了,所以在PushServlet時可以看到,這張圖比NoPushServlet中Content Download多耗了一點時間,因為要多下載一張圖。總體上相當於省下了hey.jpg建立鏈接的時間。
如果一個頁面上有很多張圖時候,使用服務器推送技術可以省下的時間也會是一個可觀的數字。
用於在運行時獲取Servlet的映射信息,它是Servlet4.0新增的接口,含有四個方法:
getMappingMatch():返回匹配的類型(如果沒有則返回null)
getMatchValue():返回匹配的值(如果沒有返回空字符串)
getPattern():與此請求匹配的url模式,如果未知則為空String。
getServletName():映射到請求的servlet的名稱(在web.xml,WebServlet.name(),ServletContext.addServlet(String,Class)或其他addServlet()指定的方法之一)
@WebServlet(value = "/myServletMapping", name = "my-servlet-mapping") public class MyServletMapping extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // HttpServletMapping用於在運行時獲取Servlet的映射信息 HttpServletMapping httpServletMapping = req.getHttpServletMapping(); // getMappingMatch返回匹配的值(如果沒有返回空字符串) MappingMatch mappingMatch = httpServletMapping.getMappingMatch(); // EXACT // 返回匹配的值(如果沒有返回空字符串) String matchValue = httpServletMapping.getMatchValue(); // myServletMapping // 與此請求匹配的url,如果未知則為空String。 String pattern = httpServletMapping.getPattern(); // /myServletMapping // 映射到請求的servlet的名稱,如果沒寫默認是全路徑名稱 String servletName = httpServletMapping.getServletName(); // my-servlet-mapping } }
Servlet4.0添加了GenericFilter和HttpFilter抽象類,這些抽象類通過提供最低限度地實現生命周期方法init()和destory(),簡化了編寫過濾器。
addJspFile() 可將帶有給定 JSP 文件的 servlet 添加到 servlet 上下文中。
getSessionTimeout() 和 setSessionTimeout() 可提供對會話超時的訪問權限。
getRequestCharacterEncoding() 和 setRequestCharacterEncoding() 可為當前的 servlet 上下文提供訪問權限,並改變默認的請求字符編碼。
HttpServletRequest 接口上的 isRequestedSessionIdFromUrl() 方法已被棄用。
由於升級到 Java SE 8,默認方法已被添加到偵聽器接口中。
ServletRequest和HttpServletRequest區別
定義一個對象以向servlet提供客戶端請求信息。servlet容器創建一個ServletRequest對象,並將其作為參數傳遞給servlet的服務方法。
ServletRequest對象提供數據,包括參數名和值、屬性和輸入流。擴展ServletRequest的接口可以提供額外的協議特定的數據(例如,HTTP數據是由HttpServletRequest提供的。
擴展ServletRequest接口,為HTTP servlet提供請求信息。
servlet容器創建一個HttpServletRequest對象,並將其作為參數傳遞給servlet的服務方法(doGet,doPost等)。
而HttpServletRequest處理提供了上面的這些行為,還支持







