嵌套組件
這些組件是針對(duì)Tocmat做的特定實(shí)現(xiàn),他們的主要目的是使各種Tomcat容器可以完成各自的工作。
1、閥(Valve)
valve是處理元素,它可以被包含在每個(gè)Tomcat容器的處理路徑中--如engine、host、context以及servelt包裝器。若要增加Valve到Tomcat容器則需要在server.xml中使用<Valve>標(biāo)簽。在server.xml中這些標(biāo)簽的執(zhí)行順序與其物理順序相同。
而在Tomcat中也分布這大量預(yù)先編譯好的valve。包括:
? 在請(qǐng)求日志元素中將請(qǐng)求(如遠(yuǎn)程客戶端ip地址)寫入日志文件或數(shù)據(jù)庫(kù)時(shí)
? 根據(jù)遠(yuǎn)程客戶端ip或主機(jī)名來(lái)控制某一特定web應(yīng)用的訪問權(quán)限時(shí)
? 記錄每個(gè)請(qǐng)求和響應(yīng)頭信息日志時(shí)
? 在同一個(gè)虛擬主機(jī)下為多個(gè)應(yīng)用配置單點(diǎn)登錄時(shí)
如果以上這些都不能滿足你的要求,那么你可以通過繼承org.apache.catalina.Valve來(lái)實(shí)現(xiàn)自定義的Valve并將其配置到對(duì)應(yīng)服務(wù)中。
但是對(duì)于一個(gè)容器來(lái)說,它并不會(huì)持有某個(gè)單獨(dú)valve的引用;相反,它會(huì)持有一個(gè)稱作管道(Pipeline)的單一實(shí)體的引用,并用這個(gè)管道來(lái)表示與該容器所關(guān)聯(lián)的一系列valve。
當(dāng)一個(gè)容器被調(diào)用來(lái)處理一個(gè)請(qǐng)求時(shí),它會(huì)委托與其關(guān)聯(lián)的管道來(lái)處理對(duì)應(yīng)請(qǐng)求。
在管道中,這些valve則是基于他們?cè)趕erver.xml中的定義作順序排列。其中排在隊(duì)列中排在最后的valve被稱為管道的基本valve,該valve用來(lái)完成去執(zhí)行容器的核心功能的任務(wù)。
與單個(gè)valve不同,管道在server . xml不是一個(gè)明確的元素,而是含蓄的按照valve在給定容器中所定義的順序組成。
并且在管道中,每個(gè)valve都知道其下一個(gè)valve;在它執(zhí)行完前置處理以后,接下來(lái)它會(huì)調(diào)用鏈中的下一個(gè)valve,當(dāng)該調(diào)用返回以后,它會(huì)在return之前執(zhí)行他自身的處理任務(wù)。
這種方式和servlet規(guī)范中的過濾器鏈所做的事情非常相似。
在這幅圖中,當(dāng)接收到傳入請(qǐng)求時(shí)引擎所配置的valve首先被觸發(fā)。其中引擎中基本的valve負(fù)責(zé)確定目標(biāo)主機(jī)委托該主機(jī)來(lái)處理;接下來(lái)目標(biāo)主機(jī)(www.host1.com)的valve被按順序觸發(fā)。而該主機(jī)的基本valve則又決定了目標(biāo)context(在這里是Context1)并且委托該context來(lái)處理該請(qǐng)求。最后Context1中所配置的valve被觸發(fā),然后通過context中配置的基本valve委托給適當(dāng)?shù)陌b器來(lái)處理;而包裝器的基本valve又將處理轉(zhuǎn)交至被包裝的servlet來(lái)處理。
處理完成以后,響應(yīng)結(jié)果會(huì)按照以上的路徑反方向返回。
由于Valve就成了Tomcat服務(wù)器實(shí)現(xiàn)中的一部分,并且可以為開發(fā)者提供一種方式將自定義的代碼注入到處理請(qǐng)求的servlet容器中。因此,自定的valve類文件需要發(fā)布到CATALINA_HOME/lib目錄下而不是應(yīng)用的發(fā)布目錄WEB-INF/classes。
由于它們并不是servlet規(guī)范中的部分,所以valve在企業(yè)級(jí)一用中屬于不可移植元素。因此,如果已經(jīng)依賴了一個(gè)特定的valve時(shí),你必須在不同的應(yīng)用服務(wù)器上找到對(duì)等的選擇方案。
還有很重要的一點(diǎn)就是,為了不影響請(qǐng)求處理的效率必須要保證valve的代碼高效執(zhí)行。
2、Realm
容器管理安全方面的工作通過容器處理應(yīng)用程序的身份驗(yàn)證和授權(quán)方面來(lái)解決。
身份驗(yàn)證存在的主要任務(wù)就是確保用戶所說的就是她自己,而授權(quán)的主要任務(wù)是決定一個(gè)用戶是否可以在某個(gè)應(yīng)用下執(zhí)行特定操作。
由容器來(lái)管理安全的優(yōu)勢(shì)是可以通過應(yīng)用的發(fā)布者直接來(lái)配置安全措施。也就是說,為用戶分配密碼以及為用戶分配角色都可以用戶配置來(lái)完成,而這些配置也可以在修改任何代碼的情況下來(lái)供多個(gè)web應(yīng)用共用。
應(yīng)用管理安全
還有一種可選方案就是通過應(yīng)用來(lái)管理安全問題。這種情況下,我的web應(yīng)用程序代碼就是唯一的仲裁者來(lái)決定用戶在我們的應(yīng)用下是否有訪問特定功能或資源的權(quán)限。
想要使容器來(lái)管理安全問題起作用,我們需要組裝一下組件:
? 安全約束:在我們的web應(yīng)用部署描述器web.xml中,我們必須確定限制資源訪問的url表達(dá)式以及可以訪問這些資源的用戶角色。
? 憑證輸入機(jī)制:在web.xml部署文件中,我們需要指定容器應(yīng)該如何提示用戶通過憑證來(lái)驗(yàn)證。這通常是通過彈出一個(gè)對(duì)話框提示用戶輸入用戶名和密碼來(lái)完成,但也可以配置使用其他機(jī)制,如一個(gè)定制的登錄表單等。
? Realm:這是一個(gè)數(shù)據(jù)存儲(chǔ)機(jī)制來(lái)保存用戶名、密碼以及角色來(lái)對(duì)用戶所提供的憑證信息進(jìn)行檢查。它可以是一個(gè)簡(jiǎn)單的xml文件,一個(gè)通過JDBC API來(lái)訪問的關(guān)系型數(shù)據(jù)庫(kù)中的一張表或者是可以通過JNDI API訪問的輕量級(jí)目錄訪問協(xié)議服務(wù)器(LDAP)。正是Realm為Tomcat提供了一致的訪問機(jī)制來(lái)訪問這些不同的數(shù)據(jù)源。
以上這三種組件在技術(shù)上是相互獨(dú)立的。基于容器安全的威力就在于我們可以根據(jù)我們自身的安全情況從這幾種方式中選出適合的一種或幾種方式來(lái)混合使用。
至此,當(dāng)一個(gè)用戶請(qǐng)求一個(gè)資源時(shí),Tomcat將檢查對(duì)所請(qǐng)求的資源是否已經(jīng)存在了安全限制。對(duì)于存在限制的資源,Tomcat將自動(dòng)要求用戶提供身份憑證并通過所配置的Realm來(lái)檢查用戶所提供憑證是否符合。只有在用戶所提供的憑證信息通過了驗(yàn)證或者用戶的角色在可訪問資源的配置之列才能訪問對(duì)應(yīng)資源。
3、執(zhí)行器
這是從tomcat 6.0.11版本開始,新增的一個(gè)組件。此組件允許您配置一個(gè)共享的線程池,以供您的連接器使用。您的連接器可能使并發(fā)線程的數(shù)量達(dá)到上限。請(qǐng)注意,此限制同樣適用于:即使一個(gè)特定的連接器沒有用完為它配置的所有線程。
4、監(jiān)聽器
每個(gè)主要的tomcat組件都實(shí)現(xiàn)了org.apache.catalina.Lifecycle接口。實(shí)現(xiàn)了該接口的組件注冊(cè)到監(jiān)聽器,然后該組件的生命周期事件被觸發(fā),比如該組件的啟動(dòng)和停止都會(huì)被監(jiān)聽。一個(gè)監(jiān)聽器實(shí)現(xiàn)了org.apache.catalina.LifecycleListener接口,也實(shí)現(xiàn)了該接口的lifecycleEvent()方法,監(jiān)聽器捕捉到一個(gè)LifecycleEvent 表示事件已經(jīng)發(fā)生。這就給您提供了一個(gè)機(jī)會(huì):把您自定義的進(jìn)程注入到tomcat的生命周期。
5、會(huì)話管理器
會(huì)話讓使用無(wú)狀態(tài)HTTP協(xié)議的應(yīng)用程序完成通信。會(huì)話表示客戶端和服務(wù)器之間的通信,會(huì)話功能是由javax.servlet.http.HttpSession 的實(shí)例實(shí)現(xiàn)的,該實(shí)例存儲(chǔ)在服務(wù)器上而且與一個(gè)唯一的標(biāo)識(shí)符相關(guān)聯(lián),客戶端在與服務(wù)器的每次交互中根據(jù)請(qǐng)求中的標(biāo)識(shí)符找到它的會(huì)話。一個(gè)新的會(huì)話在客戶端請(qǐng)求后被創(chuàng)建,會(huì)話一直有效直到一段時(shí)間后客戶端連接超時(shí),或者會(huì)話直接失效例如客戶退出訪問服務(wù)器。
?
上圖顯示了一個(gè)非常簡(jiǎn)單的 tomcat 會(huì)話機(jī)制視圖。Catalina 引擎(engine)使用了組件org.apache.catalina.Manager 去創(chuàng)建、查找、注銷會(huì)話。該組件負(fù)責(zé)管理為上下文創(chuàng)建的會(huì)話以及會(huì)話的生命周期。會(huì)話管理器(Manager)把會(huì)話存放在內(nèi)存中,但是它支持在服務(wù)器重啟時(shí)恢復(fù)會(huì)話。當(dāng)服務(wù)器停止時(shí),會(huì)話管理器把所有活動(dòng)的會(huì)話寫入磁盤,當(dāng)服務(wù)器重新啟動(dòng)時(shí)把會(huì)話重新加載到內(nèi)存。
一個(gè)<Manager>必須是 <Context>的子節(jié)點(diǎn),而且<Manager>負(fù)責(zé)管理與web應(yīng)用程序上下文相關(guān)的會(huì)話。
會(huì)話管理器管理這樣的屬性:例如用來(lái)生成會(huì)話標(biāo)識(shí)符的算法,每秒鐘檢查過期會(huì)話的頻率,支持的活動(dòng)會(huì)話的最大數(shù)目,以及持久化會(huì)話的文件。
會(huì)話管理器實(shí)現(xiàn)了這樣的功能:為會(huì)話提供持久化存儲(chǔ),例如一個(gè)文件或一個(gè)JDBC數(shù)據(jù)庫(kù)。
6、加載器
這個(gè)組件是一個(gè)給定的web應(yīng)用程序的類加載器。Java中的類加載器是一個(gè)神秘的實(shí)體。簡(jiǎn)而言之,類加載器負(fù)責(zé)加載、解釋Java類編譯后的字節(jié)碼。
一個(gè)Java類的字節(jié)碼可能存放在各種不同的位置,最常見的是在本地文件系統(tǒng)或網(wǎng)絡(luò)中。類加載器的主要任務(wù)是:抽象字節(jié)如何被獲取以及如何重組到內(nèi)存中的類的過程。
7、委托(代理)模型
自從Java 2以來(lái),類加載機(jī)制使用了委托模型,JVM中的類加載器被組織成了父---子層次結(jié)構(gòu)。據(jù)介紹,每個(gè)類加載器首先把查找和加載一個(gè)類的任務(wù)委托給它的父級(jí),在它自己嘗試做這個(gè)任務(wù)之前。
這種委托機(jī)制確保:沒有應(yīng)用程序可以加載一個(gè)有惡意的系統(tǒng)類(例如java.lang.Object),它可能危及在JVM中運(yùn)行的應(yīng)用程序的完整性。
類加載器層次結(jié)構(gòu)的頂層是Bootstrap 類加載器,它也叫原始類加載器,它是原生代碼而且是JVM的一部分。作為JVM的一部分可以保證:至少有一個(gè)可以依靠的類加載器,去加載Java的核心類(例如java.lang.Object)。這個(gè)類加載器(Bootstrap )負(fù)責(zé)從Java核心包(例如java.lang 或 java.util)里加載類文件。在SUN的JVM中,這些核心類文件存放在JAVA_HOME/jre/lib/rt.jar 。Bootstrap類加載器的獨(dú)特之處在于:它是層次結(jié)構(gòu)樹的根節(jié)點(diǎn),因此它沒有父類加載器。
層次結(jié)構(gòu)的下一層是Extension類加載器,它在SUN的JVM中是一個(gè)java.net.URLClassLoader,它監(jiān)控 JAVA_HOME/jre/lib/ext 文件夾擴(kuò)展JARs 。放在此文件夾下的任何JARs(包括類路徑之外的)都會(huì)被自動(dòng)加載。
層次結(jié)構(gòu)最底層是系統(tǒng)類加載器(應(yīng)用程序類加載器),它在SUN的JVM中也是一個(gè)URLClassLoader 。它監(jiān)控CLASSPATH里描述的文件夾、JARs 。這個(gè)類加載器負(fù)責(zé)加載應(yīng)用程序的主類。
如果一個(gè)普通的應(yīng)用程序需要加載一個(gè)類(例如java.lang.String),它首先會(huì)詢問應(yīng)用程序類加載器去加載那個(gè)類。應(yīng)用程序類加載器會(huì)委托給它的父級(jí)Extension類加載器,Extension類加載器委托給它的父級(jí)Bootstrap 類加載器,Bootstrap 類加載器然后在rt.jar里加載String.class文件并且使它成為一個(gè)可用的java.lang.Class實(shí)例。
如果需要加載一個(gè)應(yīng)用程序特定的類文件(例如com.swengsol.UserModel.class),那么委托過程和前面一樣。然而這次,Bootstrap 類加載器在rt.jar里沒能加載到此類文件,現(xiàn)在輪到Extension類加載器同樣沒能加載到此類文件。最后,應(yīng)用程序類加載器在CLASSPATH里找到了此類文件。然后這個(gè)類文件被加載并成為一個(gè)JVM可以使用的實(shí)例。
每個(gè)類加載器里都有緩存,所以每個(gè)類加載器首先要檢查自己的緩存看是否已經(jīng)加載了類文件。如果找到了,則直接返回類文件。
在我們前面的示例中,如果應(yīng)用程序需要另一個(gè)String類,那么Bootstrap 類加載器則直接返回在它緩存里的String類實(shí)例。
?
未完待續(xù)......
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
