前言
其實javaWeb的知識早就學過了,可是因為現在在搞大數據開發,所以web的知識都忘記了。准備開始慢慢的把Web的知識一點一點的回憶起來,多學一點沒有關系,就怕到時候要用的話,什么都不會了。
一、Servlet概述
1.1、Servlet簡介
Servlet 運行在服務端的Java小程序,是sun公司提供一套規范(接口),用來處理客戶端請求、響應給瀏覽器的動態資源。但servlet的實質就是java代碼,通過java的API 動態的向客戶端輸出內容。
Servlet是Java Web的三大組件(Servlet,Filter,Listener)之一,屬於動態資源 ,運行在 Web 服務器或應用服務器上的程序作用為處理請求,服務器會把接收的請求交給Servlet來處理,在Servlet中通常需要:
接受請求數據、處理請求、完成響應
例如:客戶端發出登錄請求,或輸出注冊請求,這些請求都應該有Servlet來完成處理。每個Servlet都必須實現javax.servle.Servlet接口。
總結:
處理請求和發送響應的過程是由一種叫做Servlet的程序來完成的,並且Servlet是為了解決實現動態頁面而衍生的東西。理解這個的前提是了解一些http協議的東西,並且知道B/S模式(瀏覽器/服務器)。
B/S:瀏覽器/服務器。 瀏覽器通過網址來訪問服務器,比如訪問百度,在瀏覽器中輸入www.baidu.com,這個時候瀏覽器就會顯示百度的首頁。
補充1:
servlet規范(sun公司自己制定了一種用於擴展web服務器功能的組件規范):包含三個技術點(三大組件)
servlet技術
filter技術---過濾器
listener技術---監聽器
1)擴展web服務器功能
web服務器(tomcat、Weblogic、iis、apache)沒有處理動態資源請求的能力(即該請求需要計算),只能處理靜態資源的請求(如果瀏覽器請求某個html頁面,
web服務器查看請求的html頁面是否存在,存在則返回。)如果要讓web服務器處理動態資源的請求,則需要使用cgi程序、組件加容器的方式。
2)組件(可以單獨部署的軟件模塊,組件必須要符合相應的規范。)
優點是可以加快軟件開發的速度,提高軟件的可維護性。容器:為組件提供運行環境,並且管理組件的生命周期。組件並不完全依賴特定的容器,只要符合相應的規范就可以。
補充2:
1.2、實現Servlet的方式
實現javax.servlet.Servlet接口
繼承javax.servlet.GenericServlet類
繼承javax.servlet.http.HttpServlet類
通常會去繼承HttpServlet類來完成Servlet
1.3、Servlet與線程安全問題
一個類型的Servlet只有一個實例對象,那么就有可能會出現一個Servlet同時處理多個請求,線程不安全,但Servlet工作效率高。
解決方案:
不要在Servlet中創建成員,創建局部變量變量即可!
可以創建無狀態成員!
可以創建有狀態的成員,但狀態必須位為只讀的!

public class Servlet extends HttpServlet { //無狀態成員 /*public class User { public void hello() { System.out.println("Hello"); } }*/ //創建有狀態的成員,但狀態必須位為只讀的 /*public class User { private String name = "zhangsan"; public String getName() { return name; } }*/ private User user = new User(); protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("doPost()..."); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("doGet()..."); } }
二、tomcat和Servlet的聯系
Tomcat 是Web應用服務器,是一個Servlet/JSP容器. Tomcat 作為Servlet容器,負責處理客戶請求,把請求傳送給Servlet,並將Servlet的響應傳送回給客戶.而Servlet是一種運行在支持Java語言的服務器上的組件.。
Servlet最常見的用途是擴展Java Web服務器功能,提供非常安全的,可移植的,易於使用的CGI替代品。
從http協議中的請求和響應可以得知,瀏覽器發出的請求是一個請求文本,而瀏覽器接收到的也應該是一個響應文本。
但是在上面這個圖中,並不知道是如何轉變的,只知道瀏覽器發送過來的請求也就是request,我們響應回去的就用response。忽略了其中的細節,現在就來探究一下。
1)Tomcat將http請求文本接收並解析,然后封裝成HttpServletRequest類型的request對象,所有的HTTP頭數據讀可以通過request對象調用對應的方法查詢到。
2)Tomcat同時會要響應的信息封裝為HttpServletResponse類型的response對象,通過設置response屬性就可以控制要輸出到瀏覽器的內容,然后將response交給tomcat,tomcat就會將其變成響應文本的格式發送給瀏覽器。
Java Servlet API 是Servlet容器(tomcat)和servlet之間的接口,它定義了serlvet的各種方法,還定義了Servlet容器傳送給Servlet的對象類,其中最重要的就是ServletRequest和ServletResponse。
所以說我們在編寫servlet時,需要實現Servlet接口,按照其規范進行操作。
三、編寫一個簡單的Servlet
3.1、手動編寫一個Servlet
1)創建一個Servlet_demo_0010繼承HttpServlet,重寫doGet和doPost方法,也就是看請求的方式是get還是post,然后用不同的處理方式來處理請求。
2)在web.xml中配置Servlet_demo_0010,為什么需要配置?讓瀏覽器發出的請求知道到達哪個servlet,也就是讓tomcat將封裝好的request找到對應的servlet讓其使用。
分析:
按照步驟,首先瀏覽器通過http://localhost:4040/Web_Servlet/ServletTest來找到web.xml中的url-pattern,這就是第一步,匹配到了url-pattern后,就會找到第二步servlet的名字MyServlet,知道了名字,
就可以通過servlet-name找到第三步,到了第三步,也就能夠知道servlet的位置了。然后到其中找到對應的處理方式進行處理。
3)測試
說明配置成功了!(輸出get因為http請求默認是get請求)
3.2、使用Eclipse向導創建一個Servlet
這個就相對簡單了,web.xml不用我們手動配置,工具直接幫我們自動配置了
1)右擊項目,在new選項中有直接新建servlet的選項
2)配置Servlet_demo_0020類中的信息
3)配置web.xml中的servlet信息
4)查看Servlet_demo_0020類中的代碼和web.xml,其中的配置跟手動的配置是一樣的,只是用圖形化界面,讓我們更方便的創建servlet而產生的。
四、Servlet生命周期
4.1、生命周期方法與特性
void init(ServletConfig servletConfig):Servlet對象創建之后馬上執行的初始化方法,只執行一次;
void service(ServletRequest servletRequest, ServletResponse servletResponse):每次處理請求都是在調用這個方法,它會被調用多次;
void destroy():在Servlet被銷毀之前調用,負責釋放Servlet對象占用的資源的方法;
特性:
單例,一個類只有一個對象,當然可能存在多個Servlet類
線程不安全的,所以它的效率高。
Servlet類由自己編寫,但對象由服務器來創建,並由服務器來調用相應的方法
4.2、Servlet生命周期
服務器啟動時(web.xml中配置load-on-startup=1,默認為0)或者第一次請求該servlet時,就會初始化一個Servlet對象,也就是會執行初始化方法init(ServletConfig conf)
該servlet對象去處理所有客戶端請求,在service(ServletRequest req,ServletResponse res)方法中執行
最后服務器關閉時,才會銷毀這個servlet對象,執行destroy()方法。
圖解:
詳細說明:
總結(面試會問):
1)Servlet何時創建
默認第一次訪問servlet時創建該對象(調用init()方法)
2)Servlet何時銷毀
服務器關閉servlet就銷毀了(調用destroy()方法)
3)每次訪問必須執行的方法
public void service(ServletRequest arg0, ServletResponse arg1)
五、Servlet原理
5.1、Servlet執行過程
在瀏覽器的地址欄輸入:http://ip:port/appNames/servlet
1)通過瀏覽器和ip:port和這個服務器建立連接。
2) 瀏覽器會生成一個請求數據包(路徑appNames/servlet)向服務器發送請求。
3) 服務器收到請求數據包,分析請求資源路徑做精准定位,通過請求的appName查找webapps文件下面的appName做匹配,匹配上了需要獲取web.xml中的servlet(mapping)。
4) 服務器創建兩個對象:
第一個對象:請求對象,該對象實現了HttpServletRequest接口,服務器會將請求數據包中的數據解析出來,存儲在該對象里。這樣做的好處是沒有必要理解http協議,只需要讀取request。
第二個對象:響應對象,實現了HttpServletResponse接口,作用是servlet處理完成后的結果可以存放到該對象上,然后服務器依據該對象的數據生成響應數據包。
5) servlet在執行servlet()方法時,可以通過request獲取請求數據,也可以將處理結果存放到response上。然后服務器與響應對象直接形成一個默契,生成一個響應數據包給瀏覽器。
6)瀏覽器解析服務器返回的響應數據包,生成響應的結果。
5.2、Servlet配置
1)基本配置web.xml文件
<!--servlet的類的配置-->
<!--servlet的虛擬路徑的配置-->
其中url-pattern的配置方式:
1)完全匹配:訪問的資源與配置的資源完全相同才能訪問到
絕對地址只能映射到1個地址
格式:/目錄/目錄/文件名.擴展名
<url-pattern>/quickStartServlet</url-pattern>
2)目錄匹配:格式:/虛擬的目錄..../*
<url-pattern>/aaa/bbb/ccc/*</url-pattern> //* 代表任意
3)匹配擴展名:格式:*.擴展名;
<url-pattern>*.abcd</url-pattern>
2)服務器啟動實例化Servlet配置
Servlet何時創建:默認第一次訪問時創建
為什么是默認?
當在Servlet的配置是,加上一個配置<load-onstartup> ;
servlet對象在服務器啟動時就創建。<!--數字代表優先級,數字越小優先級越高-->
<load-on-startup>4</load-on-startup> 最好取中間數字 4/5。
3)缺省Servlet
5.3、創建的servlet是繼承自httpServlet,而不是直接實現Servlet接口的原理

GenericServlet的繼承結構,實現了Servlet接口和ServletConfig接口
Servlet接口內容
從這里可以看到,Servlet生命周期的三個關鍵方法,init、service、destroy。還有另外兩個方法,一個getServletConfig()方法來獲取ServletConfig對象,
ServletConfig對象可以獲取到Servlet的一些信息,ServletName、ServletContext、InitParameter、InitParameterNames、通過查看ServletConfig這個接口就可以知道:
ServletConfig接口內容
其中ServletContext對象是servlet上下文對象,功能有很多,獲得了ServletContext對象,就能獲取大部分我們需要的信息,比如獲取servlet的路徑,等方法。
到此,就知道了Servlet接口中的內容和作用,總結起來就是,三個生命周期運行的方法,獲取ServletConfig,而通過ServletConfig又可以獲取到ServletContext。而GenericServlet實現了Servlet接口后,
也就說明我們可以直接繼承GenericServlet,就可以使用上面我們所介紹Servlet接口中的那幾個方法了,能拿到ServletConfig,也可以拿到ServletContext,不過那樣太麻煩,不能直接獲取ServletContext,
所以GenericServlet除了實現Servlet接口外,還實現了ServletConfig接口,那樣,就可以直接獲取ServletContext了。
GenericServlet類的內容詳解
看上圖,用紅色框框起來的就是實現Servlet和ServletConfig接口所實現的方法,有9個,這很正常,但是我們可以發現,init方法有兩個,一個是帶有參數ServletConfig的,一個有無參的方法,為什么這樣設計?
這里需要知道其中做了什么事情,來看看這兩個方法分別做了什么事?
init(ServletConfig config)
init()
一個成員變量config
getServletConfig()
通過這幾個方法一起來講解,首先看init(ServletConfig config)方法,因為只有init(ServletConfig config)中帶有ServletConfig對象,為了方便能夠在其他地方也能直接使用ServletConfig對象,而不僅僅局限在init(ServletConfig config)方法中,
所以創建一個私有的成員變量config,在init(ServletConfig config)方法中就將其賦值給config,然后通過getServletConfig()方法就能夠獲取ServletConfig對象了,這個可以理解,但是在init(ServletConfig config)中,158行,還調用了一個init()方法,
並且這個init()方法是空的,什么讀沒有,這是為什么呢?這個原因是為了防止一件事情,當我們需要在init方法中做一點別的事情,我們想到的方法就是繼承GenericServlet並且重寫了init(ServletConfig config)方法,這樣依賴,
就破壞了原本在GenericServlet類中init(ServletConfig config)寫的代碼了,也就是在GenericServlet類中的成員變量config會一直是null,無法得到賦值,因為被重寫了,就不會在執行GenericServlet中init(ServletConfig config)方法中的代碼。
要想賦值,就必須在重寫的init(ServletConfig config)方法中調用父類的init(ServletConfig config)方法,也就是super.init(ServletConfig config),這樣一來,就很不方便,怕有時候會忘了寫這句代碼,所以在GenericServlet類中增加一個init()方法,
以后需要在init方法中需要初始化別的數據,只需要重寫init()這個方法,而不需要去覆蓋init(ServletConfig config)這個方法,這樣設計,就好很多,不用在管init(ServletConfig config)這個其中的內容了。也不用出現其他的問題。
service(ServletRequest req, ServletResponse res)
一個抽象方法,說明在GenericServlet類中並沒有實現該內容,那么我們想到的是,在它上面肯定還有一層,也就是還有一個子類繼承它,實現該方法,要是讓我們自己寫的Servlet繼承GenericServlet,
需要自己寫service方法,那豈不是累死,並且我們可以看到,service方法中的參數還是ServletRequest,ServletResponse。並沒有跟http相關對象掛鈎,所以我們接着往下面看。
HttpServlet類詳解
繼承了GenericServlet類,通過我們上面的推測,這個類主要的功能肯定是實現service方法的各種細節和設計。並且通過類名可以知道,該類就跟http掛鈎了。
關注service(HttpServletRequest req, HttpServletResponse resp)方法和service(ServletRequest req, ServletResponse res)方法。
service(ServletRequest req, ServletResponse res)方法
該方法中就做一件事情,就是將ServletRequest和ServletResponse這兩個對象強轉為HttpServletRequest和HttpServletResponse對象。為什么能這樣轉?
首先要知道req、res是什么類型,通過打印System.out.println(req),可以知道,req實際上的類型是org.apache.catalina.connector.RequestFacade
Tomcat中的源碼:
通過圖可以得知,req的繼承結構:RequestFacade、httpServletRequest、ServletRequest,我們知道本身req是ServletRequest,那么從繼承結構上看,它也可以看成HttpServletRequest,也可以看成ServletRequest,
所以強轉為HttpServletRequest是可以的,如果不明白,我舉個例子,ArrayList、List、Object 這個,Object obj = new ArrayList(); List list = new ArrayList(); 一個ArrayList對象可以看成List對象, 也可以看成一個Object對象,
現在obj是不是可以堪稱List對象呢?答案是可以的,因為obj就是ArrayList對象,既然是ArrayList對象,那么就可以看成是List對象。一樣的道理,RequestFacade 對應 ArrayList、httpServleRequest對應 List、 ServletRequest 對應 Object。
轉換為httpServletRequest和HttpServletResponse對象之后,在調用service(HttpServletRequest req, HttpServletResponse resp)方法。
service(HttpServletRequest req, HttpServletResponse resp)
這個方法就是判斷瀏覽器過來的請求方式是哪種,每種的處理方式不一樣,我們常用的就是get,post,並且,我們處理的方式可能有很多的內容,所以,在該方法內會將get,post等其他5種請求方式提取出來,
變成單個的方法,然后我們需要編寫servlet時,就可以直接重寫doGet或者doPost方法就行了,而不是重寫service方法,更加有針對性。所以這里就回到了我們上面編寫servlet時的情況,繼承httpServlet,
而只要重寫兩個方法,一個doGet,一個doPost,其實就是service方法會調用這兩個方法中的一個(看請求方式)。所
喜歡就點個“推薦”哦!