Servlet的調(diào)用圖
前面我們已經(jīng)學(xué)過了Servlet的生命周期了圃验,我們根據(jù)Servlet的生命周期畫出Servlet的調(diào)用圖加深理解
Servlet的細(xì)節(jié)
一個(gè)已經(jīng)注冊(cè)的Servlet可以被多次映射
同一個(gè)Servlet可以被映射到多個(gè)URL上。
<servlet>
<servlet-name>Demo1</servlet-name>
<servlet-class>zhongfucheng.web.Demo1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Demo1</servlet-name>
<url-pattern>/Demo1</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Demo1</servlet-name>
<url-pattern>/ouzicheng</url-pattern>
</servlet-mapping>
無論我訪問的是http://localhost:8080/Demo1還是http://localhost:8080/ouzicheng缝呕。我訪問的都是Demo1澳窑。
Servlet映射的URL可以使用通配符
通配符有兩種格式:
- *.擴(kuò)展名
- 正斜杠(/)開頭并以“/*”結(jié)尾斧散。
匹配所有
匹配擴(kuò)展名為.jsp的
如果.擴(kuò)展名和正斜杠(/)開頭并以“/”結(jié)尾兩種通配符同時(shí)出現(xiàn),匹配的是哪一個(gè)呢摊聋?
- 看誰的匹配度高鸡捐,誰就被選擇
-
*.
擴(kuò)展名的優(yōu)先級(jí)最低
Servlet映射的URL可以使用通配符和Servlet可以被映射到多個(gè)URL上的作用:
- 隱藏網(wǎng)站是用什么編程語言寫的【.php,.net,.asp實(shí)際上訪問的都是同一個(gè)資源】
- 用特定的后綴聲明版權(quán)【公司縮寫】
<servlet>
<servlet-name>Demo1</servlet-name>
<servlet-class>zhongfucheng.web.Demo1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Demo1</servlet-name>
<url-pattern>*.jsp</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Demo1</servlet-name>
<url-pattern>*.net</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Demo1</servlet-name>
<url-pattern>*.asp</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Demo1</servlet-name>
<url-pattern>*.php</url-pattern>
</servlet-mapping>
Servlet是單例的
為什么Servlet是單例的
瀏覽器多次對(duì)Servlet的請(qǐng)求,一般情況下麻裁,服務(wù)器只創(chuàng)建一個(gè)Servlet對(duì)象箍镜,也就是說,Servlet對(duì)象一旦創(chuàng)建了煎源,就會(huì)駐留在內(nèi)存中色迂,為后續(xù)的請(qǐng)求做服務(wù),直到服務(wù)器關(guān)閉手销。
每次訪問請(qǐng)求對(duì)象和響應(yīng)對(duì)象都是新的
對(duì)于每次訪問請(qǐng)求脚草,Servlet引擎都會(huì)創(chuàng)建一個(gè)新的HttpServletRequest請(qǐng)求對(duì)象和一個(gè)新的HttpServletResponse響應(yīng)對(duì)象,然后將這兩個(gè)對(duì)象作為參數(shù)傳遞給它調(diào)用的Servlet的service()方法原献,service方法再根據(jù)請(qǐng)求方式分別調(diào)用doXXX方法馏慨。
線程安全問題
當(dāng)多個(gè)用戶訪問Servlet的時(shí)候,服務(wù)器會(huì)為每個(gè)用戶創(chuàng)建一個(gè)線程姑隅。當(dāng)多個(gè)用戶并發(fā)訪問Servlet共享資源的時(shí)候就會(huì)出現(xiàn)線程安全問題写隶。
原則:
- 如果一個(gè)變量需要多個(gè)用戶共享,則應(yīng)當(dāng)在訪問該變量的時(shí)候讲仰,加同步機(jī)制synchronized (對(duì)象){}
- 如果一個(gè)變量不需要共享慕趴,則直接在 doGet() 或者 doPost()定義.這樣不會(huì)存在線程安全問題
load-on-startup
如果在
作用:
- 為web應(yīng)用寫一個(gè)InitServlet,這個(gè)servlet配置為啟動(dòng)時(shí)裝載鄙陡,為整個(gè)web應(yīng)用創(chuàng)建必要的數(shù)據(jù)庫表和數(shù)據(jù)
- 完成一些定時(shí)的任務(wù)【定時(shí)寫日志冕房,定時(shí)備份數(shù)據(jù)】
在web訪問任何資源都是在訪問Servlet
當(dāng)你啟動(dòng)Tomcat,你在網(wǎng)址上輸入http://localhost:8080趁矾。為什么會(huì)出現(xiàn)Tomcat小貓的頁面耙册?
這是由缺省Servlet為你服務(wù)的!
- 我們先看一下web.xml文件中的配置,web.xml文件配置了一個(gè)缺省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>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
- 什么叫做缺省Servlet毫捣?凡是在web.xml文件中找不到匹配的详拙,它們的訪問請(qǐng)求都將交給缺省Servlet處理,也就是說蔓同,缺省Servlet用于處理所有其他Servlet都不處理的訪問請(qǐng)求
- 既然我說了在web訪問任何資源都是在訪問Servlet饶辙,那么我訪問靜態(tài)資源【本地圖片,本地HTML文件】也是在訪問這個(gè)缺省Servlet【DefaultServlet】
- 證實(shí)一下:當(dāng)我沒有手工配置缺省Servlet的時(shí)候斑粱,訪問本地圖片是可以訪問得到的
- 現(xiàn)在我自己配置一個(gè)缺省Servlet,Demo1就是我手工配置的缺省Servlet弃揽,覆蓋掉web.xml配置的缺省Servlet
<servlet>
<servlet-name>Demo1</servlet-name>
<servlet-class>zhongfucheng.web.Demo1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Demo1</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
- 下面我繼續(xù)訪問一下剛才的圖片,此時(shí)輸出的是Demo1這個(gè)Servlet寫上的內(nèi)容了
- 總結(jié):無論在web中訪問什么資源【包括JSP】,都是在訪問Servlet矿微。沒有手工配置缺省Servlet的時(shí)候痕慢,你訪問靜態(tài)圖片,靜態(tài)網(wǎng)頁冷冗,缺省Servlet會(huì)在你web站點(diǎn)中尋找該圖片或網(wǎng)頁,如果有就返回給瀏覽器惑艇,沒有就報(bào)404錯(cuò)誤
ServletConfig對(duì)象
ServletConfig對(duì)象有什么用蒿辙?
通過此對(duì)象可以讀取web.xml中配置的初始化參數(shù)。
現(xiàn)在問題來了滨巴,為什么我們要把參數(shù)信息放到web.xml文件中呢思灌?我們可以直接在程序中都可以定義參數(shù)信息,搞到web.xml文件中又有什么好處呢恭取?
好處就是:能夠讓你的程序更加靈活【更換需求泰偿,更改配置文件web.xml即可,程序代碼不用改】
獲取web.xml文件配置的參數(shù)信息
- 為Demo1這個(gè)Servlet配置一個(gè)參數(shù)蜈垮,參數(shù)名是name耗跛,值是zhongfucheng
<servlet>
<servlet-name>Demo1</servlet-name>
<servlet-class>zhongfucheng.web.Demo1</servlet-class>
<init-param>
<param-name>name</param-name>
<param-value>zhongfucheng</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>Demo1</servlet-name>
<url-pattern>/Demo1</url-pattern>
</servlet-mapping>
-
在Servlet中獲取ServletConfig對(duì)象,通過ServletConfig對(duì)象獲取在web.xml文件配置的參數(shù)
image
ServletContext對(duì)象
什么是ServletContext對(duì)象攒发?
當(dāng)Tomcat啟動(dòng)的時(shí)候调塌,就會(huì)創(chuàng)建一個(gè)ServletContext對(duì)象。它代表著當(dāng)前web站點(diǎn)
ServletContext有什么用惠猿?
- ServletContext既然代表著當(dāng)前web站點(diǎn)羔砾,那么所有Servlet都共享著一個(gè)ServletContext對(duì)象,所以Servlet之間可以通過ServletContext實(shí)現(xiàn)通訊偶妖。
- ServletConfig獲取的是配置的是單個(gè)Servlet的參數(shù)信息姜凄,ServletContext可以獲取的是配置整個(gè)web站點(diǎn)的參數(shù)信息
- 利用ServletContext讀取web站點(diǎn)的資源文件
- 實(shí)現(xiàn)Servlet的轉(zhuǎn)發(fā)【用ServletContext轉(zhuǎn)發(fā)不多,主要用request轉(zhuǎn)發(fā)】
Servlet之間實(shí)現(xiàn)通訊
ServletContext對(duì)象可以被稱之為域?qū)ο?/strong>
到這里可能有一個(gè)疑問趾访,域?qū)ο笫鞘裁茨靥恚科鋵?shí)域?qū)ο罂梢院唵卫斫獬?strong>一個(gè)容器【類似于Map集合】
實(shí)現(xiàn)Servlet之間通訊就要用到ServletContext的setAttribute(String name,Object obj)方法,
第一個(gè)參數(shù)是關(guān)鍵字扼鞋,第二個(gè)參數(shù)是你要存儲(chǔ)的對(duì)象
- 這是Demo2的代碼
//獲取到ServletContext對(duì)象
ServletContext servletContext = this.getServletContext();
String value = "zhongfucheng";
//MyName作為關(guān)鍵字屿聋,value作為值存進(jìn) 域?qū)ο蟆绢愋陀贛ap集合】
servletContext.setAttribute("MyName", value);
- 這是Demo3的代碼
//獲取ServletContext對(duì)象
ServletContext servletContext = this.getServletContext();
//通過關(guān)鍵字獲取存儲(chǔ)在域?qū)ο蟮闹? String value = (String) servletContext.getAttribute("MyName");
System.out.println(value);
- 訪問Demo3可以獲取Demo2存儲(chǔ)的信息,從而實(shí)現(xiàn)多個(gè)Servlet之間通訊
獲取web站點(diǎn)配置的信息
如果我想要讓所有的Servlet都能夠獲取到連接數(shù)據(jù)庫的信息藏鹊,不可能在web.xml文件中每個(gè)Servlet中都配置一下润讥,這樣代碼量太大了!并且會(huì)顯得非常啰嗦冗余盘寡。
- web.xml文件支持對(duì)整個(gè)站點(diǎn)進(jìn)行配置參數(shù)信息【所有Servlet都可以取到該參數(shù)信息】
<context-param>
<param-name>name</param-name>
<param-value>zhongfucheng</param-value>
</context-param>
- Demo4代碼
//獲取到ServletContext對(duì)象
ServletContext servletContext = this.getServletContext();
//通過名稱獲取值
String value = servletContext.getInitParameter("name");
System.out.println(value);
- 試一下Demo3是否能拿到楚殿,相同的代碼
//獲取到ServletContext對(duì)象
ServletContext servletContext = this.getServletContext();
//通過名稱獲取值
String value = servletContext.getInitParameter("name");
System.out.println(value);
讀取資源文件
第一種方式:
- 現(xiàn)在我要通過Servlet111讀取1.png圖片
- 按我們以前的方式,代碼應(yīng)該是這樣的。
FileInputStream fileInputStream = new FileInputStream("1.png");
System.out.println(fileInputStream);
- 當(dāng)我們?cè)L問的時(shí)候脆粥,卻出錯(cuò)了砌溺!說找不到1.png文件
- 這是為什么呢?我們以前讀取文件的時(shí)候变隔,如果程序和文件在同一包名规伐,可以直接通過文件名稱獲取得到的!匣缘,原因很簡單猖闪,以前我們寫的程序都是通過JVM來運(yùn)行的,而現(xiàn)在肌厨,我們是通過Tomcat來運(yùn)行的
- 根據(jù)web的目錄規(guī)范培慌,Servlet編譯后的class文件是存放在WEB-INF\classes文件夾中的
- 看到這里,我們知道了要進(jìn)入classes目錄中讀取文件柑爸,所以我們將代碼改成以下方式
FileInputStream fileInputStream = new FileInputStream("D:\\zhongfucheng\\web\\WEB-INF\\classes\\zhongfucheng\\web\\1.png");
System.out.println(fileInputStream);
- 再去讀取時(shí)吵护,就發(fā)現(xiàn)可以獲取到文件了。
- 但是現(xiàn)在問題又來了表鳍,我讀取文件的時(shí)候都要寫上絕對(duì)路徑馅而,這樣太不靈活了。試想一下譬圣,如果我將該讀取文件的模塊移到其他的web站點(diǎn)上用爪,我的代碼就又要修改了【因?yàn)閣eb站點(diǎn)的名字不一樣】。
- 我們通過ServletContext讀取就可以避免修改代碼的情況胁镐,因?yàn)镾ervletContext對(duì)象是根據(jù)當(dāng)前web站點(diǎn)而生成的
- 代碼如下所示:
//獲取到ServletContext對(duì)象
ServletContext servletContext = this.getServletContext();
//調(diào)用ServletContext方法獲取到讀取文件的流
InputStream inputStream = servletContext.getResourceAsStream("/WEB-INF/classes/zhongfucheng/web/1.png");
第二種方式:
- 如果我的文件放在web目錄下偎血,那么就簡單得多了!,直接通過文件名稱就能獲取
- 代碼如下所示
//獲取到ServletContext對(duì)象
ServletContext servletContext = this.getServletContext();
//調(diào)用ServletContext方法獲取到讀取文件的流
InputStream inputStream = servletContext.getResourceAsStream("2.png");
第三種方式:
通過類裝載器讀取資源文件盯漂。
- 我的文件放在了src目錄下【也叫做類目錄】
- 代碼如下所示
//獲取到類裝載器
ClassLoader classLoader = Servlet111.class.getClassLoader();
//通過類裝載器獲取到讀取文件流
InputStream inputStream = classLoader.getResourceAsStream("3.png");
- 我的文件放在了src目錄下的包下
- 代碼如下颇玷,添加包名路徑即可。
//獲取到類裝載器
ClassLoader classLoader = Servlet111.class.getClassLoader();
//通過類裝載器獲取到讀取文件流
InputStream inputStream = classLoader.getResourceAsStream("/zhongfucheng/web/1.png");
原則:如果文件太大就缆,就不能用類裝載器的方式去讀取帖渠,會(huì)導(dǎo)致內(nèi)存溢出
如果文章有錯(cuò)的地方歡迎指正,大家互相交流竭宰。習(xí)慣在微信看技術(shù)文章的同學(xué)空郊,可以關(guān)注微信公眾號(hào):Java3y