




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
1、Servlet 工作原理解析許令波, Java 工程師, 淘寶網(wǎng)簡介Web技術(shù)成為當(dāng)今主流的互聯(lián)網(wǎng)Web應(yīng)用技術(shù)之一,而Servlet是Java Web技術(shù)的核心基礎(chǔ)。因而掌握Servlet的工作原理是成為一名合格的Java Web技術(shù)開發(fā)人員的基本要求。本文將帶你認(rèn)識(shí)Java Web技術(shù)是如何基于Servlet工作,你將知道:以Tomcat為例了解Servlet容器是如何工作的?一個(gè)Web工程在Servlet容器中是如何啟動(dòng)的?Servlet容器如何解析你在web.xml中定義的Servlet?用戶的請(qǐng)求是如何被分配給指定的Servlet的?Servlet容器如何管理Servlet生命周期?
2、你還將了解到最新的Servlet的API的類層次結(jié)構(gòu),以及Servlet中一些難點(diǎn)問題的分析。從Servlet容器說起要介紹Servlet必須要先把Servlet容器說清楚,Servlet與Servlet容器的關(guān)系有點(diǎn)像槍和子彈的關(guān)系,槍是為子彈而生,而子彈又讓槍有了殺傷力。雖然它們是彼此依存的,但是又相互獨(dú)立發(fā)展,這一切都是為了適應(yīng)工業(yè)化生產(chǎn)的結(jié)果。從技術(shù)角度來說是為了解耦,通過標(biāo)準(zhǔn)化接口來相互協(xié)作。既然接口是連接Servlet與Servlet容器的關(guān)鍵,那我們就從它們的接口說起。前面說了Servlet容器作為一個(gè)獨(dú)立發(fā)展的標(biāo)準(zhǔn)化產(chǎn)品,目前它的種類很多,但是它們都有自己的市場定位,很難說誰優(yōu)
3、誰劣,各有特點(diǎn)。例如現(xiàn)在比較流行的Jetty,在定制化和移動(dòng)領(lǐng)域有不錯(cuò)的發(fā)展,我們這里還是以大家最為熟悉Tomcat為例來介紹Servlet容器如何管理Servlet。Tomcat本身也很復(fù)雜,我們只從Servlet與Servlet容器的接口部分開始介紹,關(guān)于Tomcat的詳細(xì)介紹可以參考我的另外一篇文章Tomcat系統(tǒng)架構(gòu)與模式設(shè)計(jì)分析。Tomcat的容器等級(jí)中,Context容器是直接管理Servlet在容器中的包裝類Wrapper,所以 Context容器如何運(yùn)行將直接影響Servlet的工作方式。圖1. Tomcat容器模型從上圖可以看出Tomcat的容器分為四個(gè)等級(jí),真正管理Serv
4、let的容器是Context容器,一個(gè)Context對(duì)應(yīng)一個(gè)Web工程,在Tomcat的配置文件中可以很容易發(fā)現(xiàn)這一點(diǎn),如下:清單 1 Context 配置參數(shù) 下面詳細(xì)介紹一下Tomcat解析Context容器的過程,包括如何構(gòu)建Servlet的過程。Servlet容器的啟動(dòng)過程Tomcat7也開始支持嵌入式功能,增加了一個(gè)啟動(dòng)類org.apache.catalina.startup.Tomcat。創(chuàng)建一個(gè)實(shí)例對(duì)象并調(diào)用start方法就可以很容易啟動(dòng)Tomcat,我們還可以通過這個(gè)對(duì)象來增加和修改Tomcat的配置參數(shù),如可以動(dòng)態(tài)增加Context、Servlet等。下面我們就利用這個(gè)Tom
5、cat類來管理新增的一個(gè)Context容器,我們就選擇Tomcat7自帶的examples Web工程,并看看它是如何加到這個(gè)Context容器中的。清單2. 給Tomcat增加一個(gè)Web工程Tomcat tomcat = getTomcatInstance(); File appDir = new File(getBuildDirectory(), webapps/examples); tomcat.addWebapp(null, /examples, appDir.getAbsolutePath(); tomcat.start(); ByteChunk res = getUrl(http:
6、/localhost: + getPort() + /examples/servlets/servlet/HelloWorldExample); assertTrue(res.toString().indexOf(Hello World!) 0); 清單1的代碼是創(chuàng)建一個(gè)Tomcat實(shí)例并新增一個(gè)Web應(yīng)用,然后啟動(dòng) Tomcat 并調(diào)用其中的一個(gè) HelloWorldExample Servlet,看有沒有正確返回預(yù)期的數(shù)據(jù)。Tomcat 的addWebapp方法的代碼如下:清單3. Tomcat.addWebapppublic Context addWebapp(Host host, St
7、ring url, String path) silence(url); Context ctx = new StandardContext(); ctx.setPath( url ); ctx.setDocBase(path); if (defaultRealm = null) initSimpleAuth(); ctx.setRealm(defaultRealm); ctx.addLifecycleListener(new DefaultWebXmlListener(); ContextConfig ctxCfg = new ContextConfig(); ctx.addLifecycl
8、eListener(ctxCfg); ctxCfg.setDefaultWebXml(org/apache/catalin/startup/NO_DEFAULT_XML); if (host = null) getHost().addChild(ctx); else host.addChild(ctx); return ctx; 前面已經(jīng)介紹了一個(gè)Web應(yīng)用對(duì)應(yīng)一個(gè)Context容器,也就是Servlet運(yùn)行時(shí)的Servlet容器,添加一個(gè)Web 應(yīng)用時(shí)將會(huì)創(chuàng)建一個(gè)StandardContext容器,并且給這個(gè)Context容器設(shè)置必要的參數(shù),url和path分別代表這個(gè)應(yīng)用在Tomcat中的
9、訪問路徑和這個(gè)應(yīng)用實(shí)際的物理路徑,這個(gè)兩個(gè)參數(shù)與清單1中的兩個(gè)參數(shù)是一致的。其中最重要的一個(gè)配置是ContextConfig,這個(gè)類將會(huì)負(fù)責(zé)整個(gè)Web應(yīng)用配置的解析工作,后面將會(huì)詳細(xì)介紹。最后將這個(gè)Context容器加到父容器Host中。接下去將會(huì)調(diào)用Tomcat的start方法啟動(dòng)Tomcat,如果你清楚Tomcat的系統(tǒng)架構(gòu),你會(huì)容易理解Tomcat的啟動(dòng)邏輯,Tomcat的啟動(dòng)邏輯是基于觀察者模式設(shè)計(jì)的,所有的容器都會(huì)繼承Lifecycle接口,它管理者容器的整個(gè)生命周期,所有容器的的修改和狀態(tài)的改變都會(huì)由它去通知已經(jīng)注冊(cè)的觀察者(Listener),關(guān)于這個(gè)設(shè)計(jì)模式可以參考Tomcat
10、的系統(tǒng)架構(gòu)與設(shè)計(jì)模式,第二部分:設(shè)計(jì)模式。Tomcat啟動(dòng)的時(shí)序圖可以用圖 2 表示。上圖描述了Tomcat啟動(dòng)過程中,主要類之間的時(shí)序關(guān)系,下面我們將會(huì)重點(diǎn)關(guān)注添加examples應(yīng)用所對(duì)應(yīng)的StandardContext容器的啟動(dòng)過程。當(dāng)Context容器初始化狀態(tài)設(shè)為init時(shí),添加在Contex容器的Listener將會(huì)被調(diào)用。ContextConfig繼承了 LifecycleListener接口,它是在調(diào)用清單3時(shí)被加入到StandardContext容器中。ContextConfig類會(huì)負(fù)責(zé)整個(gè) Web應(yīng)用的配置文件的解析工作。ContextConfig的init方法將會(huì)主要完成
11、以下工作:創(chuàng)建用于解析xml配置文件的contextDigester對(duì)象讀取默認(rèn)context.xml配置文件,如果存在解析它讀取默認(rèn)Host配置文件,如果存在解析它讀取默認(rèn)Context自身的配置文件,如果存在解析它設(shè)置Context的 DocBaseContextConfig的init方法完成后,Context容器的會(huì)執(zhí)行startInternal方法,這個(gè)方法啟動(dòng)邏輯比較復(fù)雜,主要包括如下幾個(gè)部分:創(chuàng)建讀取資源文件的對(duì)象創(chuàng)建ClassLoader對(duì)象設(shè)置應(yīng)用的工作目錄啟動(dòng)相關(guān)的輔助類如:logger、realm、resources 等修改啟動(dòng)狀態(tài),通知感興趣的觀察者(Web 應(yīng)用的配置)
12、子容器的初始化獲取ServletContext 并設(shè)置必要的參數(shù)初始化“l(fā)oad on startup”的ServletWeb應(yīng)用的初始化工作Web應(yīng)用的初始化工作是在ContextConfig的configureStart方法中實(shí)現(xiàn)的,應(yīng)用的初始化主要是要解析 web.xml文件,這個(gè)文件描述了一個(gè)Web應(yīng)用的關(guān)鍵信息,也是一個(gè)Web應(yīng)用的入口。Tomcat首先會(huì)找globalWebXml這個(gè)文件的搜索路徑是在engine的工作目錄下尋找以下兩個(gè)文件中的任一個(gè)org/apache/catalin/startup/NO_DEFAULT_XML或conf/web.xml。接著會(huì)找hostWebX
13、ml這個(gè)文件可能會(huì)在 System.getProperty(catalina.base)/conf/$EngineName/$HostName/web.xml.default,接著尋找應(yīng)用的配置文件 examples/WEB-INF/web.xml。web.xml文件中的各個(gè)配置項(xiàng)將會(huì)被解析成相應(yīng)的屬性保存在WebXml對(duì)象中。如果當(dāng)前應(yīng)用支持Servlet3.0,解析還將完成額外9項(xiàng)工作,這個(gè)額外的9項(xiàng)工作主要是為Servlet3.0新增的特性,包括jar包中的META-INF/web-fragment.xml的解析以及對(duì)annotations的支持。接下去將會(huì)將WebXml對(duì)象中的屬性設(shè)置
14、到Context容器中,這里包括創(chuàng)建Servlet對(duì)象、filter、listener等等。這段代碼在WebXml的configureContext方法中。下面是解析Servlet的代碼片段:清單4. 創(chuàng)建Wrapper實(shí)例for (ServletDef servlet : servlets.values() Wrapper wrapper = context.createWrapper(); String jspFile = servlet.getJspFile(); if (jspFile != null) wrapper.setJspFile(jspFile); if (servlet.
15、getLoadOnStartup() != null) wrapper.setLoadOnStartup(servlet.getLoadOnStartup().intValue(); if (servlet.getEnabled() != null) wrapper.setEnabled(servlet.getEnabled().booleanValue(); wrapper.setName(servlet.getServletName(); Map params = servlet.getParameterMap(); for (Entry entry : params.entrySet()
16、 wrapper.addInitParameter(entry.getKey(), entry.getValue(); wrapper.setRunAs(servlet.getRunAs(); Set roleRefs = servlet.getSecurityRoleRefs(); for (SecurityRoleRef roleRef : roleRefs) wrapper.addSecurityReference( roleRef.getName(), roleRef.getLink(); wrapper.setServletClass(servlet.getServletClass(
17、); MultipartDef multipartdef = servlet.getMultipartDef(); if (multipartdef != null) if (multipartdef.getMaxFileSize() != null & multipartdef.getMaxRequestSize()!= null & multipartdef.getFileSizeThreshold() != null) wrapper.setMultipartConfigElement(new MultipartConfigElement( multipartdef.getLocatio
18、n(), Long.parseLong(multipartdef.getMaxFileSize(), Long.parseLong(multipartdef.getMaxRequestSize(), Integer.parseInt( multipartdef.getFileSizeThreshold(); else wrapper.setMultipartConfigElement(new MultipartConfigElement( multipartdef.getLocation(); if (servlet.getAsyncSupported() != null) wrapper.s
19、etAsyncSupported( servlet.getAsyncSupported().booleanValue(); context.addChild(wrapper); 這段代碼清楚的描述了如何將Servlet包裝成Context容器中的StandardWrapper,這里有個(gè)疑問,為什么要將Servlet包裝成StandardWrapper而不直接是Servlet對(duì)象。這里StandardWrapper是Tomcat容器中的一部分,它具有容器的特征,而Servlet為了一個(gè)獨(dú)立的web開發(fā)標(biāo)準(zhǔn),不應(yīng)該強(qiáng)耦合在Tomcat中。除了將Servlet包裝成StandardWrapper并作
20、為子容器添加到Context中,其它的所有web.xml屬性都被解析到Context中,所以說Context容器才是真正運(yùn)行Servlet的Servlet容器。一個(gè)Web應(yīng)用對(duì)應(yīng)一個(gè)Context容器,容器的配置屬性由應(yīng)用的web.xml指定,這樣我們就能理解web.xml到底起到什么作用了。創(chuàng)建Servlet實(shí)例前面已經(jīng)完成了Servlet的解析工作,并且被包裝成StandardWrapper添加在Context容器中,但是它仍然不能為我們工作,它還沒有被實(shí)例化。下面我們將介紹Servlet對(duì)象是如何創(chuàng)建的,以及如何被初始化的。創(chuàng)建Servlet對(duì)象如果Servlet的load-on-sta
21、rtup配置項(xiàng)大于0,那么在Context容器啟動(dòng)的時(shí)候就會(huì)被實(shí)例化,前面提到在解析配置文件時(shí)會(huì)讀取默認(rèn)的globalWebXml,在conf下的web.xml文件中定義了一些默認(rèn)的配置項(xiàng),其定義了兩個(gè)Servlet,分別是: org.apache.catalina.servlets.DefaultServlet和org.apache.jasper.servlet.JspServlet它們的load-on-startup分別是1和3,也就是當(dāng)Tomcat啟動(dòng)時(shí)這兩個(gè)Servlet就會(huì)被啟動(dòng)。創(chuàng)建Servlet實(shí)例的方法是從Wrapper.loadServlet開始的。loadServlet方法
22、要完成的就是獲取servletClass然后把它交給InstanceManager去創(chuàng)建一個(gè)基于servletClass.class的對(duì)象。如果這個(gè)Servlet配置了jsp-file,那么這個(gè)servletClass就是conf/web.xml中定義的org.apache.jasper.servlet.JspServlet了。創(chuàng)建Servlet對(duì)象的相關(guān)類結(jié)構(gòu)圖如下:圖3. 創(chuàng)建 Servlet 對(duì)象的相關(guān)類結(jié)構(gòu)初始化Servlet初始化Servlet在StandardWrapper的initServlet方法中,這個(gè)方法很簡單就是調(diào)用Servlet的init 的方法,同時(shí)把包裝了Stand
23、ardWrapper對(duì)象的StandardWrapperFacade作為ServletConfig傳給Servlet。Tomcat容器為何要傳StandardWrapperFacade給Servlet對(duì)象將在后面做詳細(xì)解析。如果該Servlet關(guān)聯(lián)的是一個(gè)jsp文件,那么前面初始化的就是JspServlet,接下去會(huì)模擬一次簡單請(qǐng)求,請(qǐng)求調(diào)用這個(gè)jsp文件,以便編譯這個(gè)jsp文件為class,并初始化這個(gè)class。這樣Servlet對(duì)象就初始化完成了,事實(shí)上Servlet從被web.xml中解析到完成初始化,這個(gè)過程非常復(fù)雜,中間有很多過程,包括各種容器狀態(tài)的轉(zhuǎn)化引起的監(jiān)聽事件的觸發(fā)、各種訪
24、問權(quán)限的控制和一些不可預(yù)料的錯(cuò)誤發(fā)生的判斷行為等等。我們這里只抓了一些關(guān)鍵環(huán)節(jié)進(jìn)行闡述,試圖讓大家有個(gè)總體脈絡(luò)。下面是這個(gè)過程的一個(gè)完整的時(shí)序圖,其中也省略了一些細(xì)節(jié)。圖 4. 初始化 Servlet 的時(shí)序圖Servlet體系結(jié)構(gòu)我們知道Java Web應(yīng)用是基于Servlet規(guī)范運(yùn)轉(zhuǎn)的,那么Servlet本身又是如何運(yùn)轉(zhuǎn)的呢?為何要設(shè)計(jì)這樣的體系結(jié)構(gòu)。圖5. Servlet頂層類關(guān)聯(lián)圖從上圖可以看出Servlet規(guī)范就是基于這幾個(gè)類運(yùn)轉(zhuǎn)的,與Servlet主動(dòng)關(guān)聯(lián)的是三個(gè)類,分別是 ServletConfig、ServletRequest和ServletResponse。這三個(gè)類都是通過容
25、器傳遞給Servlet的,其中 ServletConfig是在Servlet初始化時(shí)就傳給Servlet了,而后兩個(gè)是在請(qǐng)求達(dá)到時(shí)調(diào)用Servlet時(shí)傳遞過來的。我們很清楚ServletRequest和ServletResponse在Servlet運(yùn)行的意義,但是ServletConfig和ServletContext對(duì)Servlet有何價(jià)值?仔細(xì)查看ServletConfig接口中聲明的方法發(fā)現(xiàn),這些方法都是為了獲取這個(gè)Servlet的一些配置屬性,而這些配置屬性可能在Servlet運(yùn)行時(shí)被用到。而ServletContext又是干什么的呢?Servlet 的運(yùn)行模式是一個(gè)典型的“握手型的交
26、互式”運(yùn)行模式。所謂“握手型的交互式”就是兩個(gè)模塊為了交換數(shù)據(jù)通常都會(huì)準(zhǔn)備一個(gè)交易場景,這個(gè)場景一直跟隨個(gè)這個(gè)交易過程直到這個(gè)交易完成為止。這個(gè)交易場景的初始化是根據(jù)這次交易對(duì)象指定的參數(shù)來定制的,這些指定參數(shù)通常就會(huì)是一個(gè)配置類。所以對(duì)號(hào)入座,交易場景就由ServletContext 來描述,而定制的參數(shù)集合就由 ServletConfig 來描述。而 ServletRequest 和 ServletResponse 就是要交互的具體對(duì)象了,它們通常都是作為運(yùn)輸工具來傳遞交互結(jié)果。ServletConfig 是在 Servlet init 時(shí)由容器傳過來的,那么 ServletConfig
27、到底是個(gè)什么對(duì)象呢?下圖是 ServletConfig 和 ServletContext 在 Tomcat 容器中的類關(guān)系圖。圖 6. ServletConfig 在容器中的類關(guān)聯(lián)圖上圖可以看出StandardWrapper和StandardWrapperFacade都實(shí)現(xiàn)了ServletConfig接口,而 StandardWrapperFacade是 StandardWrapper門面類。所以傳給Servlet的是StandardWrapperFacade對(duì)象,這個(gè)類能夠保證從StandardWrapper中拿到ServletConfig所規(guī)定的數(shù)據(jù),而又不把ServletConfig不關(guān)
28、心的數(shù)據(jù)暴露給Servlet。同樣ServletContext也與ServletConfig有類似的結(jié)構(gòu),Servlet中能拿到的ServletContext的實(shí)際對(duì)象也是ApplicationContextFacade對(duì)象。ApplicationContextFacade同樣保證ServletContex只能從容器中拿到它該拿的數(shù)據(jù),它們都起到對(duì)數(shù)據(jù)的封裝作用,它們使用的都是門面設(shè)計(jì)模式。通過ServletContext可以拿到Context 容器中一些必要信息,比如應(yīng)用的工作路徑,容器支持的 Servlet 最小版本等。Servlet中定義的兩個(gè)ServletRequest和Servlet
29、Response它們實(shí)際的對(duì)象又是什么呢?,我們?cè)趧?chuàng)建自己的 Servlet類時(shí)通常使用的都是HttpServletRequest和HttpServletResponse,它們繼承了ServletRequest 和 ServletResponse。為何Context容器傳過來的ServletRequest、ServletResponse 可以被轉(zhuǎn)化為 HttpServletRequest 和 HttpServletResponse呢?圖7. Request相關(guān)類結(jié)構(gòu)圖上圖是Tomcat創(chuàng)建的Request和Response的類結(jié)構(gòu)圖。Tomcat一接受到請(qǐng)求首先將會(huì)創(chuàng)建 org.apache.
30、coyote.Request和org.apache.coyote.Response,這兩個(gè)類是Tomcat內(nèi)部使用的描述一次請(qǐng)求和相應(yīng)的信息類它們是一個(gè)輕量級(jí)的類,它們作用就是在服務(wù)器接收到請(qǐng)求后,經(jīng)過簡單解析將這個(gè)請(qǐng)求快速的分配給后續(xù)線程去處理,所以它們的對(duì)象很小,很容易被JVM回收。接下去當(dāng)交給一個(gè)用戶線程去處理這個(gè)請(qǐng)求時(shí)又創(chuàng)建org.apache.catalina.connector.Request和org.apache.catalina.connector.Response對(duì)象。這兩個(gè)對(duì)象一直穿越整個(gè)Servlet容器直到要傳給Servlet,傳給Servlet的是Request和Re
31、sponse的門面類RequestFacade和RequestFacade,這里使用門面模式與前面一樣都是基于同樣的目的封裝容器中的數(shù)據(jù)。一次請(qǐng)求對(duì)應(yīng)的Request和Response的類轉(zhuǎn)化如下圖所示:圖8. Request和Response的轉(zhuǎn)變過程Servlet如何工作我們已經(jīng)清楚了Servlet是如何被加載的、Servlet是如何被初始化的,以及Servlet的體系結(jié)構(gòu),現(xiàn)在的問題就是它是如何被調(diào)用的。當(dāng)用戶從瀏覽器向服務(wù)器發(fā)起一個(gè)請(qǐng)求,通常會(huì)包含如下信息:http:/hostname:port/contextpath/servletpath,hostname和port是用來與服務(wù)器建
32、立TCP連接,而后面的URL才是用來選擇服務(wù)器中那個(gè)子容器服務(wù)用戶的請(qǐng)求。那服務(wù)器是如何根據(jù)這個(gè)URL來達(dá)到正確的Servlet容器中的呢?Tomcat7.0中這件事很容易解決,因?yàn)檫@種映射工作有專門一個(gè)類來完成的,這個(gè)就是org.apache.tomcat.util.http.mapper,這個(gè)類保存了Tomcat的Container容器中的所有子容器的信息,當(dāng)org.apache.catalina.connector.Request類在進(jìn)入Container容器之前,mapper會(huì)根據(jù)這次請(qǐng)求的hostnane和contextpath將host和context容器設(shè)置到Request的ma
33、ppingData 屬性中。所以當(dāng)Request進(jìn)入Container容器之前,它要訪問那個(gè)子容器這時(shí)就已經(jīng)確定了。圖 9.Request 的 Mapper 類關(guān)系圖可能你有疑問,mapper中怎么會(huì)有容器的完整關(guān)系,這要回到圖2中19步MapperListener 類的初始化過程,下面是MapperListener的init方法代碼 :清單 5. MapperListener.initpublic void init() findDefaultHost(); Engine engine = (Engine) connector.getService().getContainer(); eng
34、ine.addContainerListener(this); Container conHosts = engine.findChildren(); for (Container conHost : conHosts) Host host = (Host) conHost; if (!LifecycleState.NEW.equals(host.getState() host.addLifecycleListener(this); registerHost(host); 這段代碼的作用就是將MapperListener類作為一個(gè)監(jiān)聽者加到整個(gè)Container容器中的每個(gè)子容器中,這樣只要任
35、何一個(gè)容器發(fā)生變化,MapperListener都將會(huì)被通知,相應(yīng)的保存容器關(guān)系的MapperListener 的 mapper屬性也會(huì)修改。for循環(huán)中就是將host及下面的子容器注冊(cè)到mapper中。圖 10.Request 在容器中的路由圖上圖描述了一次Request請(qǐng)求是如何達(dá)到最終的Wrapper容器的,我們現(xiàn)正知道了請(qǐng)求是如何達(dá)到正確的Wrapper容器,但是請(qǐng)求到達(dá)最終的Servlet還要完成一些步驟,必須要執(zhí)行Filter鏈,以及要通知你在web.xml中定義的listener。接下去就要執(zhí)行Servlet的service方法了,通常情況下,我們自己定義的servlet并不是直
36、接去實(shí)現(xiàn)javax.servlet.servlet接口,而是去繼承更簡單的HttpServlet類或者GenericServlet類,我們可以有選擇的覆蓋相應(yīng)方法去實(shí)現(xiàn)我們要完成的工作。Servlet的確已經(jīng)能夠幫我們完成所有的工作了,但是現(xiàn)在的web應(yīng)用很少有直接將交互全部頁面都用 servlet來實(shí)現(xiàn),而是采用更加高效的MVC框架來實(shí)現(xiàn)。這些MVC框架基本的原理都是將所有的請(qǐng)求都映射到一個(gè)Servlet,然后去實(shí)現(xiàn)service方法,這個(gè)方法也就是MVC框架的入口。當(dāng)Servlet從Servlet容器中移除時(shí),也就表明該Servlet的生命周期結(jié)束了,這時(shí)Servlet的destroy方法
37、將被調(diào)用,做一些掃尾工作。Session與Cookie前面我們已經(jīng)說明了Servlet如何被調(diào)用,我們基于Servlet來構(gòu)建應(yīng)用程序,那么我們能從Servlet獲得哪些數(shù)據(jù)信息呢?Servlet能夠給我們提供兩部分?jǐn)?shù)據(jù),一個(gè)是在Servlet初始化時(shí)調(diào)用init方法時(shí)設(shè)置的ServletConfig,這個(gè)類基本上含有了Servlet本身和Servlet所運(yùn)行的Servlet容器中的基本信息。根據(jù)前面的介紹ServletConfig的實(shí)際對(duì)象是StandardWrapperFacade,到底能獲得哪些容器信息可以看看這類提供了哪些接口。還有一部分?jǐn)?shù)據(jù)是由ServletRequest類提供,它的
38、實(shí)際對(duì)象是RequestFacade,從提供的方法中發(fā)現(xiàn)主要是描述這次請(qǐng)求的 HTTP 協(xié)議的信息。所以要掌握Servlet的工作方式必須要很清楚HTTP協(xié)議,如果你還不清楚趕緊去找一些參考資料。關(guān)于這一塊還有一個(gè)讓很多人迷惑的Session與Cookie。Session與Cookie不管是對(duì)Java Web的熟練使用者還是初學(xué)者來說都是一個(gè)令人頭疼的東西。Session與Cookie的作用都是為了保持訪問用戶與后端服務(wù)器的交互狀態(tài)。它們有各自的優(yōu)點(diǎn)也有各自的缺陷。然而具有諷刺意味的是它們優(yōu)點(diǎn)和它們的使用場景又是矛盾的,例如使用Cookie來傳遞信息時(shí),隨著Cookie個(gè)數(shù)的增多和訪問量的增加
39、,它占用的網(wǎng)絡(luò)帶寬也很大,試想假如Cookie占用200個(gè)字節(jié),如果一天的PV有幾億的時(shí)候,它要占用多少帶寬。所以大訪問量的時(shí)候希望用Session,但是Session的致命弱點(diǎn)是不容易在多臺(tái)服務(wù)器之間共享,所以這也限制了Session的使用。不管Session和Cookie有什么不足,我們還是要用它們。下面詳細(xì)講一下,Session如何基于Cookie來工作。實(shí)際上有三種方式能可以讓Session正常工作:基于URL Path Parameter,默認(rèn)就支持基于Cookie,如果你沒有修改Context容器個(gè)cookies標(biāo)識(shí)的話,默認(rèn)也是支持的基于SSL,默認(rèn)不支持,只有connector
40、.getAttribute(SSLEnabled)為TRUE時(shí)才支持第一種情況下,當(dāng)瀏覽器不支持Cookie功能時(shí),瀏覽器會(huì)將用戶的SessionCookieName重寫到用戶請(qǐng)求的URL參數(shù)中,它的傳遞格式如/path/Servlet;name=value;name2=value2?Name3=value3,其中“Servlet;”后面的 K-V 對(duì)就是要傳遞的Path Parameters,服務(wù)器會(huì)從這個(gè)Path Parameters中拿到用戶配置的 SessionCookieName。關(guān)于這個(gè)SessionCookieName,如果你在web.xml中配置session-config配置
41、項(xiàng)的話,其cookie-config下的 name 屬性就是這個(gè)SessionCookieName值,如果你沒有配置session-config配置項(xiàng),默認(rèn)的SessionCookieName就是大家熟悉的“JSESSIONID”。接著Request根據(jù)這個(gè)SessionCookieName到Parameters拿到Session ID并設(shè)置到request.setRequestedSessionId中。請(qǐng)注意如果客戶端也支持Cookie的話,Tomcat仍然會(huì)解析Cookie中的Session ID,并會(huì)覆蓋URL中的Session ID。如果是第三種情況的話將會(huì)根據(jù)javax.servle
42、t.request.ssl_session屬性值設(shè)置Session ID。有了Session ID服務(wù)器端就可以創(chuàng)建HttpSession對(duì)象了,第一次觸發(fā)是通過request.getSession()方法,如果當(dāng)前的Session ID還沒有對(duì)應(yīng)的HttpSession對(duì)象那么就創(chuàng)建一個(gè)新的,并將這個(gè)對(duì)象加到 org.apache.catalina.Manager的sessions容器中保存,Manager類將管理所有Session的生命周期,Session過期將被回收,服務(wù)器關(guān)閉,Session 將被序列化到磁盤等。只要這個(gè)HttpSession對(duì)象存在,用戶就可以根據(jù) Session I
43、D來獲取到這個(gè)對(duì)象,也就達(dá)到了狀態(tài)的保持。圖 11.Session 相關(guān)類圖上從圖中可以看出從request.getSession中獲取的HttpSession對(duì)象實(shí)際上是StandardSession對(duì)象的門面對(duì)象,這與前面的Request和Servlet是一樣的原理。下圖是Session工作的時(shí)序圖:圖 12.Session 工作的時(shí)序圖還有一點(diǎn)與Session關(guān)聯(lián)的Cookie與其它Cookie沒有什么不同,這個(gè)配置的配置可以通過web.xml中的session-config配置項(xiàng)來指定。Servlet中的Listener整個(gè)Tomcat服務(wù)器中Listener使用的非常廣泛,它是基于觀
44、察者模式設(shè)計(jì)的,Listener的設(shè)計(jì)對(duì)開發(fā)Servlet 應(yīng)用程序提供了一種快捷的手段,能夠方便的從另一個(gè)縱向維度控制程序和數(shù)據(jù)。目前Servlet中提供了5種兩類事件的觀察者接口,它們分別是:4 個(gè) EventListeners類型的,ServletContextAttributeListener、ServletRequestAttributeListener、ServletRequestListener、HttpSessionAttributeListener 和2個(gè) LifecycleListeners 類型的,ServletContextListener、HttpSessionLis
45、tener。如下圖所示:圖 13.Servlet 中的 Listener它們基本上涵蓋了整個(gè)Servlet生命周期中,你感興趣的每種事件。這些Listener的實(shí)現(xiàn)類可以配置在web.xml中的標(biāo)簽中。當(dāng)然也可以在應(yīng)用程序中動(dòng)態(tài)添加Listener,需要注意的是ServletContextListener在容器啟動(dòng)之后就不能再添加新的,因?yàn)樗O(jiān)聽的事件已經(jīng)不會(huì)再出現(xiàn)。掌握這些Listener的使用,能夠讓我們的程序設(shè)計(jì)的更加靈活??偨Y(jié)本文涉及到內(nèi)容有點(diǎn)多,要把每個(gè)細(xì)節(jié)都說清楚,似乎不可能,本文試著從Servlet容器的啟動(dòng)到Servlet的初始化,以及Servlet的體系結(jié)構(gòu)等這些環(huán)節(jié)中找出
46、一些重點(diǎn)來講述,目的是能讀者有一個(gè)總體的完整的結(jié)構(gòu)圖,同時(shí)也詳細(xì)分析了其中的一些難點(diǎn)問題,希望對(duì)大家有所幫助。文章來源 HYPERLINK /developerworks/cn/java/j-lo-servlet/ /developerworks/cn/java/j-lo-servlet/附錄資料:JAVA工程師EJB面試題集JAVA工程師EJB面試題集 HYPERLINK /phrase/200604241156485.html t _new EJB ( HYPERLINK /phrase/200603091138035.html t _new Enterprise JavaBean)是 H
47、YPERLINK /phrase/200603091447335.html t _new J2EE的一部分,定義了一個(gè)用于開發(fā)基于 HYPERLINK /phrase/200603302222545.html t _new 組件的企業(yè)多重應(yīng)用 HYPERLINK /phrase/200604232224305.html t _new 程序的標(biāo)準(zhǔn)。其特點(diǎn)包括網(wǎng)絡(luò)服務(wù)支持和核心開發(fā)工具(SDK)。 在J2EE里,Enterprise Java Beans(EJB)稱為Java 企業(yè)柄,是Java的核心代碼,分為整體柄和片段柄和 HYPERLINK /phrase/200603090938465.h
48、tml t _new 消息柄三個(gè)部分,其中的消息柄將在以后再作討論。現(xiàn)在我們來看看什么是整體柄和片段柄。 整體柄是一種 HYPERLINK /phrase/200603090845215.html t _new 對(duì)象: 標(biāo)準(zhǔn)Java對(duì)象由創(chuàng)建它的程序創(chuàng)建,當(dāng)程序終止時(shí),對(duì)象也隨之丟失,這就意味著當(dāng)再次運(yùn)行些程序時(shí),將無法找到先前創(chuàng)建的柄,而整體柄會(huì)一直存在著直到它被刪除。 一個(gè)程序可以創(chuàng)建一個(gè)整體柄,并且這個(gè)程序可以在被保存后隨時(shí)停止和重啟。整體柄將會(huì)依然存在。重啟后,程序可以找到與之相對(duì)應(yīng)的整體柄,并且會(huì)繼續(xù)使用這個(gè)整體柄。 EJB實(shí)際上是SUN的J2EE中的一套規(guī)范,并且規(guī)定了一系列的 H
49、YPERLINK /phrase/200604241228185.html t _new API用來實(shí)現(xiàn)把EJB概念轉(zhuǎn)換成EJB產(chǎn)品.EJB是BEANS,BEANS是什么概念,那就是得有一個(gè)容納她,讓她可勁造騰的地方,就是得有容器.EJB必須生存在EJB容器中.這個(gè)容器可是功能強(qiáng)大之極!她首先要包裝你BEAN,EJB的客戶程序?qū)嶋H上從來就不和你編寫的EJB直接打交道,他們之間是通過HOME/REMOTE接口來發(fā)生關(guān)系的.它負(fù)責(zé)你的BEAN的所有的吃喝拉薩睡,比如BEAN的持續(xù)化,安全性,事務(wù)管理. 一.什么是 EJB?一個(gè)技術(shù)規(guī)范:EJB 從技術(shù)上而言不是一種產(chǎn)品EJB 是一種標(biāo)準(zhǔn)描述了構(gòu)建應(yīng)
50、用組件要解決的:可擴(kuò)展 (Scalable)分布式 (Distributed)事務(wù)處理 (Transactional)數(shù)據(jù)存儲(chǔ) (Persistent)安全性 (Secure)二.Sun 對(duì) EJB 的期望提供一個(gè)標(biāo)準(zhǔn)的分布的、基于 HYPERLINK /phrase/200604231401365.html t _new OO 的組件 HYPERLINK /phrase/200604241328115.html t _new 架構(gòu)屏蔽復(fù)雜的系統(tǒng)級(jí)功能 HYPERLINK /phrase/200603101518295.html t _new 需求Write once, run anywher
51、e與非 Java 應(yīng)用之間的互操作能力兼容 HYPERLINK /phrase/200604031336425.html t _new CORBA 標(biāo)準(zhǔn)三.為什么選擇 EJB?EJB 服務(wù)器完成繁雜的工作:應(yīng)用開發(fā)人員關(guān)注于業(yè)務(wù)邏輯的實(shí)現(xiàn)而不是底層的實(shí)現(xiàn)機(jī)制( HYPERLINK /phrase/200603090857555.html t _new 類似于 4GL 語言設(shè)計(jì)的目標(biāo))支持事務(wù)處理多個(gè)業(yè)務(wù)操作同時(shí)成功,或全部失敗可以通過在代碼外的描述來定義事務(wù)處理級(jí)別可擴(kuò)展性EJB 可以根據(jù)您應(yīng)用的增長而擴(kuò)展EJB 服務(wù)器往往還提供了負(fù)載均衡和安全性:由 EJB 服務(wù)器提供資源的訪問權(quán)限控制四.
52、EJB 架構(gòu)為了滿足架構(gòu)的目標(biāo),規(guī)范中描述了服務(wù)器 (Server)容器 (Container)類 ( HYPERLINK /phrase/200604231359565.html t _new Class) 和實(shí)例 (Instance)Home 和 Remote 接口 HYPERLINK /phrase/200603082208195.html t _new 客戶端 ( HYPERLINK /phrase/200604231337375.html t _new Client)五. 簡化的編程模型關(guān)注于業(yè)務(wù)邏輯實(shí)現(xiàn):EJB 負(fù)責(zé)生命周期 (lifecycle), 數(shù)據(jù)存儲(chǔ) (persisten
53、ce), 事務(wù)處理語義 (transactional semantic), 安全(security), .通用的編程模型:各種服務(wù)的高層 APIJava 是其編程語言1.EJB 特點(diǎn)由一個(gè) EJB 容器在運(yùn)行時(shí)創(chuàng)建和管理 EJB在部署 EJB 時(shí)定制其運(yùn)行方式由 EJB 容器和服務(wù)器來協(xié)調(diào)客戶端的訪問可以部署到任何兼容的 EJB 容器中客戶端對(duì) EJB 的 HYPERLINK /phrase/200603141659315.html t _new 視圖是由 Bean 開發(fā)人員決定的2.EJB 服務(wù)器管理 EJB 容器 (它管理 Bean)提供對(duì) HYPERLINK /phrase/200602
54、281634075.html t _new 操作系統(tǒng)服務(wù)的存取提供 Java 相關(guān)的服務(wù),尤其是通過 JNDI 訪問命名空間基于 OTS 的事務(wù)處理服務(wù)3.EJB 容器管理 Bean 生命周期:將 EJB 服務(wù)器提供的服務(wù)傳遞給 Bean生成代碼來實(shí)現(xiàn)對(duì) Bean 的存取訪問強(qiáng)制事務(wù)處理的限制創(chuàng)建、初始化和回收 Bean管理持久數(shù)據(jù)的存儲(chǔ)對(duì)客戶端而言 EJB 容器是透明的4.在一個(gè) EJB 服務(wù)器中的容器目前容器通常是由 EJB 服務(wù)器本身提供的在 EJB 1.0 或 1.1 規(guī)范中沒有定義容器-到-服務(wù)器的接口各廠商可以根據(jù)他們的見解來實(shí)現(xiàn)服務(wù)器和容器的各自責(zé)任5.容器提供服務(wù): 數(shù)據(jù)存儲(chǔ)容
55、器決定何時(shí)載入/儲(chǔ)存狀態(tài)Container-Managed Persistence(容器管理存儲(chǔ)/CMP)容器負(fù)責(zé)存儲(chǔ)您的 Bean容器生成必要的類和代碼Bean-Managed Persistence(Bean 管理存儲(chǔ)/BMP)Bean 開發(fā)人員提供存儲(chǔ)代碼開發(fā)人員決定 如何存儲(chǔ), 容器仍然決定 何時(shí)進(jìn)行6.容器提供服務(wù): 事務(wù)處理可以由容器代理來實(shí)現(xiàn)容器將得到業(yè)務(wù)邏輯方法的事務(wù)處理需求容器提供事務(wù)控制代碼也可以由程序員通過代碼實(shí)現(xiàn)7.容器提供服務(wù): 其它服務(wù)其它服務(wù)包括命名 (Naming)安全 (Security) HYPERLINK /phrase/200603091754305.h
56、tml t _new 線程管理 ( HYPERLINK /phrase/200604231348385.html t _new Thread management)這些服務(wù)由容器代理完成將減少應(yīng)用開發(fā)人員的負(fù)擔(dān)8.分布式對(duì)象運(yùn)算遠(yuǎn)程對(duì)象被作為本地對(duì)象來處理:傳遞信息的方式不變,但開銷更大Enterprise JavaBeans 永遠(yuǎn)運(yùn)行在服務(wù)器上:對(duì) Bean 的訪問永遠(yuǎn)是遠(yuǎn)程調(diào)用9.Stub 和 Skeleton由 EJB 生成:Stub 對(duì)要傳遞出去的信息編碼Tie/Skel 將接受到的信息解碼并傳遞給目標(biāo)對(duì)象10.分類: Enterprise HYPERLINK /phrase/2006
57、04251741535.html t _new JavaBeans+-Entity Beans-CMP/BMPEjb-|+-Session Beans-Stateful/Stateless會(huì)話 Bean (Session Bean):根據(jù) EJB 規(guī)范,一個(gè)會(huì)話 Bean 是:代表單個(gè)客戶端來執(zhí)行可以參與到事務(wù)處理中不直接代表共享于 HYPERLINK /phrase/200602271218062.html t _new 數(shù)據(jù)庫中的數(shù)據(jù),但它能訪問和更新這些數(shù)據(jù)相對(duì)而言是短暫存在的當(dāng) EJB 容器失效后就不存在客戶端需要重新建立一個(gè)信新的會(huì)話對(duì)象來繼續(xù)運(yùn)算實(shí)體 Bean (Entity Be
58、an):根據(jù) EJB 規(guī)范,一個(gè)實(shí)體 Bean 是:提供在數(shù)據(jù)庫中數(shù)據(jù)的對(duì)象視圖允許被多個(gè)用戶共享存取訪問可以是長期存在 (只要它存在于數(shù)據(jù)庫中)實(shí)體 Bean, 它的主鍵對(duì)象, 以及它的遠(yuǎn)程引用將能跨 EJB 容器的宕機(jī)而存在11.EJB 類和實(shí)例構(gòu)建 EJB 應(yīng)用包括來自三方的代碼開發(fā)人員編寫的代碼由 EJB API 定義的類和接口由容器自動(dòng)生成的代碼開發(fā)人員編寫的代碼包括Bean 類 (定義了業(yè)務(wù)邏輯)Home 接口 (如何查找或創(chuàng)建 bean)Remote 接口 (如何存取 bean)其它組件,根據(jù) bean 實(shí)際要求12.EJB Home 接口每個(gè) bean 有一個(gè)用于:創(chuàng)建新的 b
59、ean 實(shí)例、查找現(xiàn)存的 bean (只能是實(shí)體 bean)Remote 接口:定義 bean 的公共接口只有在 Remote 接口中定義的方法才能被客戶端訪問EJB 客戶端可以為 HYPERLINK /phrase/200603091005185.html t _new servlet, JSP, 應(yīng)用程序或其它 bean通過 JNDI 來查找 EJB home 接口,步驟為:創(chuàng)建一個(gè) JNDI Context (initial context)使用 JNDI Context 來查找 bean home 接口使用 bean home 接口來創(chuàng)建/查找 bean 實(shí)例使用 bean 實(shí)例完成業(yè)
60、務(wù)操作實(shí)際的存取 (對(duì) EJB) 是通過容器生成的類來完成EJB 架構(gòu)客戶端對(duì) bean 訪問永遠(yuǎn)不是直接的EJBObject (tie) 是由容器自身提供的:用來幫助管理 bean 的生命周期EJB 中的角色EJB 服務(wù)器供應(yīng)商: 開發(fā)并銷售 EJB 服務(wù)器EJB 容器供應(yīng)商: 開發(fā)并銷售 EJB 容器Enterprise bean 開發(fā)人員: 開發(fā)并銷售 EJB應(yīng)用組裝人員: 將不同的 EJB 搭建成應(yīng)用六、EJB的 HYPERLINK /phrase/200603122156385.html t _new 體系結(jié)構(gòu) 目前,EJB最新的標(biāo)準(zhǔn)是2.1,EJB3.0規(guī)范正在討論中,預(yù)計(jì)將于明年
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 墩身安全施工方案
- 永年冷庫施工方案
- 基礎(chǔ)回填土施工方案
- 化工廠施工方案
- 二零二五年度環(huán)??萍紗挝唤獬齽趧?dòng)合同及綠色技術(shù)轉(zhuǎn)移協(xié)議
- 2025年度超市超市商品防損員勞動(dòng)合同范本
- 二零二五年度蘇州市全日制勞動(dòng)合同員工休息與休假規(guī)定合同
- 二零二五年度農(nóng)村土地占用與農(nóng)村文化傳承合同協(xié)議
- 二零二五年度婚姻忠誠保證協(xié)議:男方出軌責(zé)任書
- 二零二五年度個(gè)人車輛抵押汽車貸款合同續(xù)簽合同
- 2024年批次杭州市教育局所屬事業(yè)單位招聘筆試真題
- 2024年海東市第二人民醫(yī)院自主招聘專業(yè)技術(shù)人員考試真題
- 2025年湖南環(huán)境生物職業(yè)技術(shù)學(xué)院單招職業(yè)技能測試題庫及答案一套
- 14 文言文二則 學(xué)弈 教學(xué)設(shè)計(jì)-2024-2025學(xué)年語文六年級(jí)下冊(cè)統(tǒng)編版
- Unit 4 Eat Well(大單元教學(xué)設(shè)計(jì))2024-2025學(xué)年七年級(jí)英語下冊(cè)同步備課系列(人教版2024)
- 2024-2030年中國游戲直播行業(yè)市場深度分析及投資策略研究報(bào)告
- 統(tǒng)編版小學(xué)語文六年級(jí)下冊(cè)第四單元《理想和信念》作業(yè)設(shè)計(jì)
- 2025年春季學(xué)期學(xué)校工作計(jì)劃及安排表
- 化驗(yàn)班組安全培訓(xùn)
- 英語-廣東省大灣區(qū)2025屆高三第一次模擬試卷和答案
- 丹佛斯變頻器培訓(xùn)經(jīng)典課件
評(píng)論
0/150
提交評(píng)論