轉(zhuǎn)載:Servlet/Tomcat/ Spring 之間的關(guān)系
0.基礎(chǔ)知識(shí)
在idea中打開servlet的源碼:
可以看見servlet就是一個(gè)接口纵装;接口就是規(guī)定了一些規(guī)范相满,使得一些具有某些共性的類都能實(shí)現(xiàn)這個(gè)接口,從而都遵循某些規(guī)范录择。
有的人往往以為就是servlet直接處理客戶端的http請(qǐng)求拔莱,其實(shí)并不是這樣,servlet并不會(huì)去監(jiān)聽8080端口隘竭;直接與客戶端打交道是“容器”塘秦,比如常用的tomcat。
客戶端的請(qǐng)求直接打到tomcat动看,它監(jiān)聽端口尊剔,請(qǐng)求過來后,根據(jù)url等信息菱皆,確定要將請(qǐng)求交給哪個(gè)servlet去處理须误,然后調(diào)用那個(gè)servlet的service方法,service方法返回一個(gè)response對(duì)象仇轻,tomcat再把這個(gè)response返回給客戶端霹期。
1. Servlet的生命周期
從創(chuàng)建到毀滅:
調(diào)用init()方法初始化
調(diào)用service()方法來處理客戶端的請(qǐng)求
調(diào)用destroy()方法釋放資源,標(biāo)記自身為可回收
被垃圾回收器回收
由上面可以看見拯田,servlet的init方法和destroy方法历造,一般容器調(diào)用這兩個(gè)方法之間的過程,就叫做servlet的生命周期船庇。
調(diào)用的整個(gè)過程就如上圖所示吭产。
當(dāng)請(qǐng)求來容器第一次調(diào)用某個(gè)servlet時(shí),需要先初始化init()鸭轮,
但當(dāng)某個(gè)請(qǐng)求再次打到給servlet時(shí)臣淤,容器會(huì)起多個(gè)線程同時(shí)訪問一個(gè)servlet的service()方法。
由此可以看出窃爷,多個(gè)客戶訪問同一service()方法邑蒋,會(huì)涉及線程安全的問題姓蜂。
如果service()方法沒有訪問Servlet的成員變量也沒有訪問全局的資源比如靜態(tài)變量、文件医吊、數(shù)據(jù)庫連接等钱慢,而是只使用了當(dāng)前線程自己的資源,比如非指向全局資源的臨時(shí)變量卿堂、request和response對(duì)象等束莫。該方法本身就是線程安全的,不必進(jìn)行任何的同步控制草描。
如果service()方法訪問了Servlet的成員變量览绿,但是對(duì)該變量的操作是只讀操作,該方法本身就是線程安全的穗慕,不必進(jìn)行任何的同步控制谓谦。
如果service()方法訪問了Servlet的成員變量索昂,并且對(duì)該變量的操作既有讀又有寫嫉晶,通常需要加上同步控制語句铐然。
如果service()方法訪問了全局的靜態(tài)變量幅垮,如果同一時(shí)刻系統(tǒng)中也可能有其它線程訪問該靜態(tài)變量耿导,如果既有讀也有寫的操作箱亿,通常需要加上同步控制語句剂买。
如果service()方法訪問了全局的資源狐肢,比如文件添吗、數(shù)據(jù)庫連接等,通常需要加上同步控制語句份名。
面試問題:Servlet如何同時(shí)處理多個(gè)請(qǐng)求訪問碟联?
單實(shí)例多線程:?主要是請(qǐng)求來時(shí),會(huì)由線程調(diào)度者從線程池李取出來一個(gè)線程僵腺,來作為響應(yīng)線程鲤孵。這個(gè)線程可能是已經(jīng)實(shí)例化的,也可能是新創(chuàng)建的辰如。
Servlet容器默認(rèn)是采用單實(shí)例多線程的方式處理多個(gè)請(qǐng)求的:
1.當(dāng)web服務(wù)器啟動(dòng)的時(shí)候(或客戶端發(fā)送請(qǐng)求到服務(wù)器時(shí))普监,Servlet就被加載并實(shí)例化(只存在一個(gè)Servlet實(shí)例);
2.容器初始化化Servlet主要就是讀取配置文件(例如tomcat,可以通過servlet.xml的設(shè)置線程池中線程數(shù)目琉兜,初始化線程池通過web.xml,初始化每個(gè)參數(shù)值等等凯正。
3.當(dāng)請(qǐng)求到達(dá)時(shí),Servlet容器通過調(diào)度線程(Dispatchaer Thread) 調(diào)度它管理下線程池中等待執(zhí)行的線程(Worker Thread)給請(qǐng)求者豌蟋;
4.線程執(zhí)行Servlet的service方法廊散;
5.請(qǐng)求結(jié)束,放回線程池梧疲,等待被調(diào)用允睹;
(注意:避免使用實(shí)例變量(成員變量)运准,因?yàn)槿绻嬖诔蓡T變量,可能發(fā)生多線程同時(shí)訪問該資源時(shí)缭受,都來操作它胁澳,照成數(shù)據(jù)的不一致,因此產(chǎn)生線程安全問題)
從上面可以看出:
第一:Servlet單實(shí)例贯涎,減少了產(chǎn)生servlet的開銷听哭;
第二:通過線程池來響應(yīng)多個(gè)請(qǐng)求,提高了請(qǐng)求的響應(yīng)時(shí)間塘雳;
第三:Servlet容器并不關(guān)心到達(dá)的Servlet請(qǐng)求訪問的是否是同一個(gè)Servlet還是另一個(gè)Servlet陆盘,直接分配給它一個(gè)新的線程;如果是同一個(gè)Servlet的多個(gè)請(qǐng)求败明,那么Servlet的service方法將在多線程中并發(fā)的執(zhí)行隘马;
第四:每一個(gè)請(qǐng)求由ServletRequest對(duì)象來接受請(qǐng)求,由ServletResponse對(duì)象來響應(yīng)該請(qǐng)求妻顶;
?2. Spring?
?任何Spring Web的entry point酸员,都是servlet。
大名頂頂?shù)膕pring框架已經(jīng)風(fēng)靡多時(shí)讳嘱,一個(gè)事物的出現(xiàn)和流行都是會(huì)有原因的幔嗦,那么為什么spring 框架會(huì)出現(xiàn)呢?原因就是為了簡化java開發(fā)沥潭。
spring的核心就是通過依賴注入邀泉、面向切面編程aop、和模版技術(shù)钝鸽,解耦業(yè)務(wù)與系統(tǒng)服務(wù)汇恤,消除重復(fù)代碼。借助aop拔恰,可以將遍布應(yīng)用的關(guān)注點(diǎn)(如事物和安全)從它們的應(yīng)用對(duì)象中解耦出來因谎。
?Spring 中的Bean
?1)?POJO和JavaBean的區(qū)別 :?
"Plain Ordinary Java Object",簡單普通的java對(duì)象颜懊。主要用來指代那些沒有遵循特定的java對(duì)象模型财岔,約定或者框架的對(duì)象。
POJO的內(nèi)在含義是指那些:
有一些private的參數(shù)作為對(duì)象的屬性河爹,然后針對(duì)每一個(gè)參數(shù)定義get和set方法訪問的接口匠璧。
沒有從任何類繼承、也沒有實(shí)現(xiàn)任何接口昌抠,更沒有被其它框架侵入的java對(duì)象患朱。
JavaBean 是一種JAVA語言寫成的可重用組件。JavaBean符合一定規(guī)范編寫的Java類炊苫,不是一種技術(shù)裁厅,而是一種規(guī)范冰沙。大家針對(duì)這種規(guī)范,總結(jié)了很多開發(fā)技巧执虹、工具函數(shù)拓挥。符合這種規(guī)范的類,可以被其它的程序員或者框架使用袋励。它的方法命名侥啤,構(gòu)造及行為必須符合特定的約定:
所有屬性為private。
這個(gè)類必須有一個(gè)公共的缺省構(gòu)造函數(shù)茬故。即是提供無參數(shù)的構(gòu)造器盖灸。
這個(gè)類的屬性使用getter和setter來訪問,其他方法遵從標(biāo)準(zhǔn)命名規(guī)范磺芭。
這個(gè)類應(yīng)是可序列化的赁炎。實(shí)現(xiàn)serializable接口。
因?yàn)檫@些要求主要是靠約定而不是靠實(shí)現(xiàn)接口钾腺,所以許多開發(fā)者把JavaBean看作遵從特定命名約定的POJO徙垫。
spring中,應(yīng)用對(duì)西那個(gè)生存于spring容器中,spring 容器創(chuàng)建對(duì)象放棒,裝配它們姻报,管理它們的整個(gè)生命周期。spring容器通過依賴注入间螟,管理構(gòu)成應(yīng)用的組件吴旋,它會(huì)創(chuàng)建相互協(xié)作的組件之間的關(guān)聯(lián)。
2) Bean的生命周期
Spring MVC
?
?Spring MVC的運(yùn)行流程: