背景
近段時(shí)間打算梳理一下近一年用到的SpringBoot及部分相關(guān)starter仔戈,計(jì)劃從Web入手伐蒂。要了解Java開發(fā)的Web應(yīng)用必然的從其標(biāo)準(zhǔn)(Servlet)開始遏暴。
以下內(nèi)容基本來源Oracle官網(wǎng)對(duì)Servlet3.0的介紹垦细,由于存在個(gè)人理解偏差建議讀者直接閱讀原文盒使。
地址:https://download.oracle.com/otndocs/jcp/servlet-3.0-mrel-full-oth-JSpec
備注:登錄Oracle網(wǎng)站即可將內(nèi)容以PDF的方式下載下來(簡書不支持上傳PDF文件)
概念
- Servlet是什么矢门?
servlet是基于Java實(shí)現(xiàn)的Web組件撒桨,servlet通過servlet容器實(shí)現(xiàn)的請(qǐng)求/響應(yīng)范式煤禽。(運(yùn)行在 Web 服務(wù)器或應(yīng)用服務(wù)器上的程序液样,它是作為來自 Web 瀏覽器或其他 HTTP 客戶端的請(qǐng)求和 HTTP 服務(wù)器上的數(shù)據(jù)庫或應(yīng)用程序之間的中間層振亮。) - Servlet容器是什么巧还?
servlet容器是Web服務(wù)器或應(yīng)用程序服務(wù)器的一部分,它提供發(fā)送請(qǐng)求和響應(yīng)的網(wǎng)絡(luò)服務(wù)坊秸,基于MIME解碼請(qǐng)求麸祷,并格式化基于MIME的響應(yīng)。一個(gè)servlet容器還包含在servlet的生命周期中管理它們褒搔。
組成
- Servlet
-
Servlet接口是JavaServletAPI的核心抽象阶牍。所有Servlet通過擴(kuò)展一個(gè)實(shí)現(xiàn)接口。JavaServletAPI中實(shí)現(xiàn)Servlet接口是GenericServlet和HttpServlet星瘾。在大多數(shù)情況下走孽,開發(fā)人員將擴(kuò)展HttpServlet以實(shí)現(xiàn)他們的servlet。
1.1. 通常在開發(fā)基于HTTP的Servlet時(shí)琳状,Servlet開發(fā)人員只需關(guān)注HttpServlet中的doGet和doPost方法
1.2. JSP頁面本質(zhì)也是Servlet
屏幕截圖 2020-04-12 19.12.14.png - javax.servlet.Servlet接口(生命周期如下:)
2.1. 由Servlet容器調(diào)用磕瓷,執(zhí)行init方法初始化Servlet,只會(huì)執(zhí)行一次念逞。
2.2. 由servlet容器調(diào)用困食,執(zhí)行service方法響應(yīng)servlet請(qǐng)求,每次請(qǐng)求執(zhí)行一次翎承。注意:service方法是并發(fā)執(zhí)行陷舅,方法內(nèi)容需要考慮同步共享內(nèi)存
2.3. 由servlet容器調(diào)用,執(zhí)行destroy方法銷毀Servelt审洞,只會(huì)執(zhí)行一次莱睁。
-
屏幕截圖 2020-04-12 18.41.25.png
-
Request
- 將來自客戶端請(qǐng)求的所有信息封裝為一個(gè)Request對(duì)象。在HTTP協(xié)議中芒澜,此信息通過HTTP從客戶端傳輸?shù)椒?wù)器(注意:也可以自定義其他協(xié)議實(shí)現(xiàn))
- javax.servlet.ServletRequest接口
2.1. 方法getServerName:獲取當(dāng)前請(qǐng)求的域名或IP(www.ddblock.com)
2.2. 方法getServerPort:獲取當(dāng)前請(qǐng)求的端口(80)
2.3. 方法getRemoteAddr:獲取最后一個(gè)請(qǐng)求服務(wù)端的客戶端IP仰剿,如果使用nginx反向代理時(shí)需要特殊處理一下
2.4. 方法getRemoterHost:獲取最后一個(gè)請(qǐng)求服務(wù)端的客戶端主機(jī)名,如果使用nginx反向代理時(shí)需要特殊處理一下
2.5. 方法getProtocol:獲取當(dāng)前請(qǐng)求的協(xié)議及其版本(HTTP/1.1)
2.6. 方法getScheme:獲取當(dāng)前請(qǐng)求的具體協(xié)議(https)
2.7. 方法getCharacterEncoding:獲取請(qǐng)求體內(nèi)容的編碼格式
2.8. 方法getContentLength:獲取請(qǐng)求體內(nèi)容的字節(jié)長度
2.9. 方法getLocale:獲取客戶端接收的語言類型
2.10. 方法startAsync:將此請(qǐng)求置于異步模式痴晦,調(diào)用返回的AsyncContext對(duì)象的start執(zhí)行
2.11. 方法getDispatcherType:獲取分發(fā)器類型南吮,其中包含F(xiàn)ORWARD、 INCLUDE誊酌、REQUEST部凑、ASYNC、ERROR
2.12. 方法getServletContext:獲取上下文信息
2.13. 方法getAsyncContext:獲取異步模式下的上下文
-
ServletContext
- 定義一組servlet用來與其通信的方法碧浊。例如涂邀,要獲取文件的MIME類型,分派請(qǐng)求或?qū)懭肴罩疚募淙瘛C總€(gè)Java虛擬機(jī)的每個(gè)“web應(yīng)用程序”都有一個(gè)上下文比勉。
- javax.servlet.ServletContext接口
2.1. 方法getContextPath:獲取上下文根路徑
2.2. 方法getResourcePaths:獲取指定路徑下的下級(jí)目錄集合
2.3. 方法getMimeType:獲取指定文件的MIME類型。通過文件后綴匹配具體MIME類型,具體可參考MimeTypeMappings.properties文件(org/apache/catalina/startup/MimeTypeMappings.properties)
2.4. 方法getRequestDispatcher:獲取指定路徑的請(qǐng)求分發(fā)器浩聋,請(qǐng)求分發(fā)器通過包裝任何類型的資源響應(yīng)給客戶端
2.5. 方法addServlet:用于添加在web.xml观蜗、web-fragment.xml配置的Servlet或添加了WebListener注解的Servlet
2.6. 方法addFilter:用于添加在web.xml、web-fragment.xml配置的Filter或添加了WebFilter注解的Filter
2.7. 方法addListener:用于添加在web.xml衣洁、web-fragment.xml配置的Listener或添加了WebListener注解的ServletContextListener
-
Response
- 將響應(yīng)給客戶端的所有信息封裝為一個(gè)Response對(duì)象墓捻。在HTTP協(xié)議中,此信息從服務(wù)器傳輸?shù)娇蛻舳送ㄟ^HTTP頭或請(qǐng)求的消息體坊夫。
- javax.servlet.ServletResponse接口
2.1. 方法getCharacterEncoding:獲取響應(yīng)body內(nèi)容的編碼格式
2.2. 方法getContentType:獲取響應(yīng)body的內(nèi)容類型砖第。如:text/html; charset=UTF-8
2.3. 方法setContentLength:填充響應(yīng)客戶端的響應(yīng)body的字節(jié)長度
2.4. 方法getOutputStream:獲取ServletOutputStream對(duì)象將響應(yīng)body寫入輸出流中。getWriter方法也能實(shí)現(xiàn)相同功能践樱,但不能同時(shí)使用厂画。
2.5. 方法getWriter:獲取PrintWriter對(duì)象將響應(yīng)body寫入輸出流中凸丸。getOutputStream方法也能實(shí)現(xiàn)相同功能拷邢,但不能同時(shí)使用。
2.6. 方法setBufferSize:設(shè)置臨時(shí)存儲(chǔ)響應(yīng)內(nèi)容的緩沖區(qū)大小屎慢,如果設(shè)置比總響應(yīng)內(nèi)容小則響應(yīng)內(nèi)容會(huì)分多次響應(yīng)給客戶端
2.7. 方法flushBuffer:將臨時(shí)存儲(chǔ)響應(yīng)內(nèi)容的數(shù)據(jù)及headers都發(fā)送給客戶端
-
Filter
- 對(duì)資源(servlet或靜態(tài)內(nèi)容)的請(qǐng)求與響應(yīng)執(zhí)行過濾任務(wù)瞭稼。過濾器在doFilter方法。每個(gè)過濾器都可以訪問一個(gè)FilterConfig對(duì)象腻惠,從中可以獲取其初始化參數(shù)环肘,以及對(duì)ServletContext的引用,該引用可以用于例如加載過濾任務(wù)所需的資源
- javax.servlet.Filter接口
2.1. 由Servlet容器調(diào)用集灌,執(zhí)行init方法初始化Filter悔雹,只會(huì)執(zhí)行一次。
2.2. 由servlet容器調(diào)用欣喧,執(zhí)行doFilter方法攔截請(qǐng)求與響應(yīng)腌零,在過濾器鏈尾端執(zhí)行servlet,每次請(qǐng)求執(zhí)行一次唆阿。
2.3. 由servlet容器調(diào)用益涧,執(zhí)行destroy方法銷毀Filter,只會(huì)執(zhí)行一次驯鳖。
屏幕截圖 2020-04-14 22.24.52.png
- Session
- 提供一種在多個(gè)頁面上標(biāo)識(shí)用戶的方法請(qǐng)求或訪問網(wǎng)站并存儲(chǔ)有關(guān)該用戶的信息闲询。 servlet容器使用此接口在HTTP客戶端和HTTP服務(wù)器之間創(chuàng)建會(huì)話。會(huì)話在指定的時(shí)間段內(nèi)持續(xù)存在浅辙,并且跨越用戶的多個(gè)連接或頁面請(qǐng)求扭弧。一個(gè)會(huì)話通常對(duì)應(yīng)一個(gè)用戶,該用戶可能會(huì)多次訪問該站點(diǎn)记舆。服務(wù)器可以通過多種方式維護(hù)會(huì)話寄狼,例如使用cookie或重寫URL。
- javax.servlet.http.HttpSession接口
2.1. 方法isNew:獲取當(dāng)前請(qǐng)求是否是新的會(huì)話
2.2. 方法getMaxInactiveInterval:返回最大時(shí)間間隔(以秒為單位),servlet容器將在客戶機(jī)訪問之間保持此會(huì)話打開泊愧。在此間隔之后伊磺,Servlet容器將使會(huì)話無效
2.3. 方法invalidate:使該會(huì)話無效,然后取消綁定與之綁定的任何對(duì)象
2.4. 方法getServletContext:返回此會(huì)話所屬的ServletContext
2.5. 方法setAttribute删咱、getAttribute:用來存儲(chǔ)與獲取同一會(huì)話的數(shù)據(jù)
其它說明
- 重要注解
- @WebServlet:用于定義web應(yīng)用程序中的Servlet組件
@WebServlet(name="MyServlet", urlPatterns={"/foo", "/bar"}) public class SampleUsingAnnotationAttributes extends HttpServlet{ public void doGet(HttpServletRequest req, HttpServletResponse res) { } }
- @WebFilter:定義web應(yīng)用程序中的過濾器
@WebFilter("/foo") public class MyFilter implements Filter { public void doFilter(HttpServletRequest req, HttpServletResponse res) { } }
- @WebInitParam:用于指定必須傳遞給Servlet或Filter屑埋。是WebServlet和WebFilter的一個(gè)屬性
- @WebListener:定義對(duì)web應(yīng)用程序上下文各類事件監(jiān)聽的監(jiān)聽器。
javax.servlet.ServletContextListener javax.servlet.ServletContextAttributeListener javax.servlet.ServletRequestListener javax.servlet.ServletRequestAttributeListener javax.servlet.http.HttpSessionListener javax.servlet.http.HttpSessionAttributeListener @WebListener public class MyListener implements ServletContextListener{ public void contextInitialized(ServletContextEvent sce) { ServletContext sc = sce.getServletContext(); sc.addServlet("myServlet", "Sample servlet", "foo.bar.MyServlet", null, -1); sc.addServletMapping("myServlet", new String[] {"/urlpattern/*" }); } }
- javax.servlet.ServletContainerInitializer接口(基于代碼配置痰滋,而不是web.xml)
- 提供容器初始化的擴(kuò)展邏輯摘能。容器通過jar查找在容器/應(yīng)用程序啟動(dòng)時(shí)由容器提供的服務(wù)API(如下圖META-INF/services)∏媒郑框架提供ServletContainerInitializer的實(shí)現(xiàn)必須將一個(gè)名為ServletContainerInitializer团搞,根據(jù)jar服務(wù)API,指向ServletContainerInitializer的實(shí)現(xiàn)類多艇。除了ServletContainerInitializer之外逻恐,我們還有一個(gè)注解handleTypes。實(shí)現(xiàn)handleTypes注釋ServletContainerInitializer用于表示對(duì)可能在handletype或者如果它在類的超級(jí)類型峻黍。容器使用handleTypes注釋來確定何時(shí)調(diào)用初始值設(shè)定項(xiàng)的onStartup方法
@HandlesTypes(WebApplicationInitializer.class) public class SpringServletContainerInitializer implements ServletContainerInitializer { @Override public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException { } }
屏幕截圖 2020-04-15 23.19.56.png
2. spring-boot啟動(dòng)web容器時(shí)未按照Servlet3.0規(guī)范實(shí)現(xiàn)复隆,具體可參考(https://blog.csdn.net/weixin_34128501/article/details/92424885),其實(shí)現(xiàn)類為org.springframework.boot.web.embedded.tomcat.TomcatStarter
屏幕截圖 2020-04-15 23.30.42.png
-
javax.servlet.RequestDispatcher接口
- 表示從客戶端接收請(qǐng)求并將請(qǐng)求發(fā)送到服務(wù)器上的任何資源(例如servlet姆涩,HTML文件或JSP文件)的類挽拂。 包含forward與include兩種模式。
- forward模式:將請(qǐng)求從Servlet轉(zhuǎn)發(fā)到服務(wù)器上的另一個(gè)資源(Servlet骨饿,JSP文件或HTML文件)亏栈。這種方法允許一個(gè)Servlet對(duì)請(qǐng)求進(jìn)行初步處理,而另一種資源可以生成響應(yīng)宏赘。
- include模式:請(qǐng)求服務(wù)器的另外一個(gè)資源并將資源作為本Servlet輸出的子內(nèi)容
-
事件通知
- 事件覆蓋了ServletContext绒北、HttpSession與ServletRequest的生命周期中。
-
具體事件監(jiān)聽器(混個(gè)眼熟)
屏幕截圖 2020-04-22 23.12.26.png屏幕截圖 2020-04-22 23.12.13.png