##什麼是servlet
Servlet是sun公司提供的一種用於開發動態web資源的技術
- 其實就是一個java程序,運行在服務器上,用來接收和響應客戶端的HTTP請求
- 更多的是配合動態資源來使用,當然靜態資源也用到servlet,只不過是Tomcat裏面已經定義好了,一個DefaultServlet(優先級爲1)
##tmocat和servlet的關係
-
tomcat是web應用服務器,是一個servlet/jsp的容器
- servlet是一種運行在支持java語言例如tomcat服務器上的組件
##servlet的創建和servlet的web.xml配置
- 創建一個簡單的servlet
- 實現servlet接口,或者繼承httpservlet
- servlet的web.xml配置
- 在webContent/WEB-INF/web.xml中寫入下面的內容
<!--告訴tomca,應用裏有這個servlet,名字叫做xxxx 具體的路徑是xxxx--> <servlet> <servlet-name>hello</servlet-name> <servlet-class>com.Servlet.Servletdemo</servlet-class> </servlet> <!-- 註冊servlet的映射 url-pattern地址欄上地址 --> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping>
- 第一個servlet-name的名字可以隨便寫,不過一般與類名相同
- 第二個servlet-name與第一個的相同
- servlet-class包名.類名.class
- url-pattern 以"/"開頭
- 在webContent/WEB-INF/web.xml中寫入下面的內容
###url-paren的配置(通配符)
- 全路徑匹配
- 以/開頭 例如
- /a
- /a/b
- 以/開頭 例如
- 路徑匹配 前半段或者後半段
-
- 通配符,匹配任意的文字
- /a/*
- *.do
-
###缺省servlet
- url-pattern僅爲一個/
- 凡是在web.xml中沒有匹配的servlet-mapping元素的url,都通過servlet來處理
-
在tomcat的安裝目錄\config\web.xml文件中,註冊了一個org.apache.catalina.servlets.DefaultServlet的Servlet,並將這個Servlet設置爲了缺省Servlet。我們在訪問靜態資源就是在使用這個缺省的servlet
<servlet> <servlet-name>default</servlet-name> <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class> <init-param> <param-name>debug</param-name> <param-value>0</param-value> </init-param> <init-param> <param-name>listings</param-name> <param-value>false</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <!-- The mapping for the default servlet --> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
##servlet的訪問過程運行過程
servlet程序是由web服務器調用,web服務器接收到客戶端的servlet請求後
- web服務器檢查是否已經裝載並創建了該servlet對象,如果是就執行第4步,否則執行第二部
- 裝載並創建一個該servlet對象
- 調用servlet實例對象的init()方法
- 創建一個用於封裝HTTP請求消息的HttpServletRequest對象和一個代表HTTP響應消息的HttpServletResponse對象,然後調用Servlet的service()方法並將請求和響應對象作爲參數傳遞進去。
- web應用被重啓或者重新啓動之前,將卸載servlet,在卸載之前調用servlet的destory方法
servlet的訪問過程
- 根據要早的servlet的url 在web.xml中找到相匹配的url-pattern
- 根據找到的路徑在對應的servlet-mapping中找到servlet-name
- 根據servlet-name去找到servlet中的servlet-name
- 最後找到了對應的servlet.class文件
##servlet接口及其實現類
- Servlet接口定義了兩個默認實現類,分別是GenericServlet,HttpServlet
- HttpServlet在實現Servlet接口時,覆蓋了service方法,該方法體內的代碼會自動判斷用戶的請求方式,因此只需要重寫doGet方法或者doPost方法
- HttpServlet指能夠處理HTTP請求的servlet,在原有的servlet接口上添加了一些HTTP協議的處理方法,比Servlet接口更加強大,因此在編寫Servlet時,通常繼承這個類,而避免直接去實現Servlet接口
###爲什麼要有兩個接口
- GenericServlet雖然實現了Servlet接口,但是沒有指定所實用的協議,而HttpServlet指定了所使用的HTTP協議,SUN公司處於維護的目的,一旦出現了別的協議,就可以直接添加新的類似HttpServlet的類繼承GenericServlet
servlet(接口)
GenericServlet(重寫了5個接口) httpservlet(用於處理http的請求)
##servlet的生命週期
###生命週期
- 從創建到銷燬的時間
###生命週期方法
- 從創建到銷燬所調用的方法,都叫生命週期方法
- inti()方法
- 在創建servlet的實例時會調用init方法
- 聲明時候創建實例
- 默認情況下,初次方法是時候會創建實例
- 一個servlet只會初始化一次,也就是說init只會執行一次,servlet是一個單實例,多線程的類,只會創建一次
-
service 方法
- 只要客戶端來了一個請求,就會調用這個方法
- 該方法可以被執行多次,來一次請求就調用service方法
- destory方法
- 銷燬的時候就執行該方法
- 什麼時候銷燬
- 該項目從tomcat中移除
- 正常的關閉服務器
doGet和doPost不算生命週期方法,生命週期方法只指,從對象創建到銷燬一定會執行的方法,但這兩個方法不一定會執行
###創建的時機,前提
- 默認情況下,只有在初次訪問servlet的時候,纔會執行init方法,有的時候,我們需要在這個方法裏面執行一些初始化工作,甚至做一些比較耗時的邏輯
- 那麼這個時候,初次訪問,就可能在init方法中逗留太久時間,那麼有沒有方法讓初始化的時間提前一點
- 在配置的時候,使用load-on-starup來指定,給定的數字越小,啓動的時間就越早,一般不寫負數,從2開始,因爲1已經被tomcat裏的東西所佔用
<servlet>
<servlet-name>demo</servlet-name>
<servlet-class>com.Servlet.Demo</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
- load-on-startup
- 標記容器是否在啓動的時候就加載這個servlet
- 當值爲非負數的時候就在啓動時就加載servlet
- 當時一個負數或者沒有指定時,該servlet在選擇時加載
- 正數的值越小,啓動servlet的優先級越高
- 0比1的優先級高
- 我們一般從2開始,1是tomcat的servlet的優先級
##servlet的線程安全問題
當多個tomcat併發的訪問同一個servlet時,web服務器爲每一個客戶端的訪問請求創建一個線程,並在這個線程上調用servlet的service方法,如果service方法訪問了同一個資源的話就看你引發多線程安全問題
- (假)解決方法,加鎖保證任何時候都只有一個線程在方法servlet中的資源
- 這樣雖然解決了線程安全問題,但是很不現實,因爲這樣就變成了輪流訪問了
- (過時)sun公司提供的解決方案,讓Servlet去實現一個SingleThreadModel接口,如果某個Servlet實現了SingleThreadModel接口,那麼Servlet引擎將以單線程模式來調用其service方法。
- 對於實現了SingleThreadModel接口的Servlet,Servlet引擎仍然支持對該Servlet的多線程併發訪問,其採用的方式是產生多個Servlet實例對象,併發的每個線程分別調用一個獨立的Servlet實例對象。
- 實現SingleThreadModel接口並不能真正解決Servlet的線程安全問題,因爲Servlet引擎會創建多個Servlet實例對象,而真正意義上解決多線程安全問題是指一個Servlet實例對象被多個線程同時調用的問題。事實上,在Servlet API 2.4中,已經將SingleThreadModel標記爲Deprecated(過時的)。
- 創建多個對象會佔用服務器資源
##servletConfig
###配置servletconfig
- 在web.xml中配置
<servlet>
<servlet-name>ServletConfigDemo1</servlet-name>
<servlet-class>study.ServletConfigDemo1</servlet-class>
<!--配置ServletConfigDemo1的初始化參數 -->
<init-param>
<param-name>name</param-name>
<param-value>gacl</param-value>
</init-param>
<init-param>
<param-name>password</param-name>
<param-value>123</param-value>
</init-param>
<init-param>
<param-name>charset</param-name>
<param-value>UTF-8</param-value>
</init-param>
</servlet>
###獲取servletconfig
- 通過初始化的方法獲得servletconfig對象
public void init(ServletConfig config) throws ServletException {
this.config = config;
}
- 通過父類GenericServlet的方法獲得一個ServletConfig對象
ServletConfig config = getServletConfig()
###獲取配置的初始化參數
servlet配置了初始化參數後,web容器在創建servlet實例對象時,會自動將初始化參數封裝到servletConfig對象中,在調用init方法時,會將servletConfig對象傳遞給servlet
- 例子
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//獲取在web.xml中配置的初始化參數
String paramVal = this.config.getInitParameter("name");//獲取指定的初始化參數
response.getWriter().print(paramVal);
response.getWriter().print("<hr/>");
//獲取所有的初始化參數
Enumeration<String> e = config.getInitParameterNames();
while(e.hasMoreElements()){
String name = e.nextElement();
String value = config.getInitParameter(name);
response.getWriter().print(name + "=" + value + "<br/>");
}
}
###servletchongfig的方法
- getServletName() 獲取servlet的名稱,就是在web.xml文件中配置的
- getservletContext() 獲取servletcontext對象
- getInitParameter(String) 獲取在Servlet中初始化的參數的值,只能獲取該Servlet下面的值
- getInitParameterNames() 獲取該servlet中所有初始化參數的名字,根據名字就可以獲得各個初始化參數的value值,返回的類型是枚舉類型
值的注意的是我們可以直接調用getServletName()等方法來獲取個種參數,因爲在父類GenericServlet中,已經幫我們獲取了這些數據,不在需要我們手動調用了
###servletconfig的作用
- 未來我們自己開發的一些應用,使用了一些技術,或者一些代碼,我們不會,但有人寫出來了,天的代碼放置在了自己的servlet類中
剛好這個servlet裏面需要一個數字或者變量值,但這個值不能是固定的,所以要求使用這個servlet的公司,在註冊servlet的時候,必須在web.xml裏面,聲明init-params - 存放一些數據庫的連接啊,用戶名密碼啊,修改的時候就可以不修改源代碼,只修改配置文件就夠了
##servletcontext
###什麼是servletcongtext
有叫上下文,域對象,簡單的說一個web項目有一個servletcontext實例,每個servlet都可以訪問他
- web容器在啓動時,會爲每個web應用都創建對應的servlet實例,在服務器啓動時創建,在關閉時銷燬,在一個web項目中共享數據,管理web項目資源
- 由於一個web應用中所有servlet共享了一個servletcontext對象,因此servlet對象之間可以通過servletcontext來實現通訊,因此有被稱爲域對象
###獲取servletcontext
- 直接用父類的方法getServletContext()
- 通過servletconfig來獲取servletcontextgetServletConfig().getServletContext();
###配置參數
- 必須在web.xml的最上方
<contxet-param>
<param-name>add</param-name>
<param-value>123</param-value>
</context-param>
- 是一組鍵值對 name是鍵,value是值
- 當服務啓動的時候,會讀取web.xml文件,當讀到<context-param>這個節點,就set到我們的servletcontext中,我們就可以通過servletcontext來獲取全局配置信息
###作用
####實現數據共享
- 在當前應用,是servlet共享數據
- setAttribute(String name,Object obj) 在servletcontext中存入內容
- getAttribute(String name) 通過名稱得到內容
- removeAttribute(String name) 移除名稱的內容
####獲取配置的參數
- 通過方法getInitParameter("add")獲取
String add = servletContext.getInitParameter("add");
- 通過getInitParameters() 獲取枚舉類型
####實現請求轉發
ServletContext context = this.getServletContext();//獲取ServletContext對象
RequestDispatcher rd = context.getRequestDispatcher("要去的界面");//獲取請求轉發對象(RequestDispatcher)
rd.forward(request, response);//調用forward方法實現請求轉發
####讀取資源文件--獲得路徑
- 獲取web項目下指定資源的路徑
直接調用getServltContext().getRealPath("")
;獲得到D:\apache-tomcat-9.0.12\wtpwebapps\項目名\
- 想獲得webcontent的下文件直接加文件名就好了
getServltContext().getRealPath("文件名")
- 獲得Java Resoureces --> src下的文件
getServltContext().getRealPath("/WEB-INF/classes/文件名")
- 獲得src--->db.config包中的文件
getServletContext().getRealPath("/WEB-INF/classes/db/config/文件名");
爲什麼在classes下呢
- tomcat會把web工程下的java文件編譯後的字節碼文件.class放到classes下,會把其他的資源也存放到classes下
####讀取資源文件--獲得流
- 將獲得路徑的方法替換爲獲得流的方法
getResourceAsStream
獲取資源的流對象
####讀取資源文件--class類加載器
- 注意事項:不適合裝載大文件,否則會導致jvm內存溢出
- 類加載器得到的路徑D:\apache-tomcat-8.5.33\wtpwebapps\ServletRegister\WEB-INF\classes
- 得到src下的文件
//獲取到裝載當前類的類裝載器 ClassLoader loader = ServletContextDemo1.class.getClassLoader(); //用類裝載器讀取src目錄下的文件 InputStream in = loader.getResourceAsStream("文件名");
- 得到包下的資源文件
//獲取到裝載當前類的類裝載器 ClassLoader loader = ServletContextDemo1.class.getClassLoader(); //用類裝載器讀取src目錄下的servlet.study包中的文件 InputStream in = loader.getResourceAsStream("servlet/study/db4.properties");
- 得到webcontent下的資源文件,先向上返回兩層到工程目錄下,在進去想得到的文件
//獲取到裝載當前類的類裝載器
ClassLoader loader = ServletContextDemo1.class.getClassLoader();
//用類裝載器讀取webContent下的文件
classLoader.getResourceAsStream("../../Config.properties");
###控制瀏覽器緩存
添加Expires頭可以有效減少Http請求次數,只要瀏覽器中有緩存,並且相應的資源未過期,均可以使用緩存中的資源,無須再發送請求到服務器
public class ResponseDemo6 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/**
* 設置數據合理的緩存時間值,以避免瀏覽器頻繁向服務器發送請求,提升服務器的性能
* 這裏是將數據的緩存時間設置爲1小時
*/
response.setDateHeader("expires", System.currentTimeMillis()+1000*3600);
String data = "aaaaaaaaaa";
response.getWriter().write(data);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}