JAVA-WEB目錄(許令波版)

第一章 深入Web請求過程

B/S網(wǎng)絡(luò)架構(gòu)概述

  • 瀏覽器發(fā)起一個URL請求的過程
  • CDN內(nèi)容分布網(wǎng)絡(luò)

如何發(fā)起一個請求

  • 發(fā)起一個Http請求的過程就是建立一個Socket通信的過程勾效,只不過outputStream.write寫的二進制字節(jié)數(shù)據(jù)格式要符合Http協(xié)議
  • 瀏覽器嘹悼、HttpClient、Linux curl命令都能發(fā)起Http請求

Http解析

  • Http請求頭:Accept-CharSet, Accept-Encoding, Accept-Language, Host, User-Agent
  • Http響應(yīng)頭:Server层宫,Content-Type, Content-Encoding, Content-Language, Content-Length, Keep-Alive
  • Http狀態(tài)碼:200,302,400,403,404,500
  • 瀏覽器緩存機制:Cache-Control/Pragma

DNS域名解析

  • DNS域名解析過程
    1) 瀏覽器緩存
    2) 操作系統(tǒng)緩存
    3) LDNS本地域名服務(wù)器
    4)Root Server根域名服務(wù)器 -> gTLD Server 主域名服務(wù)器 -> Name Server 域名注冊服務(wù)器(域名-IP映射表)
    5)LDNS緩存結(jié)果并返回給用戶
  • 幾種域名解析方式
    1)A記錄:Address記錄绘迁,域名解析成IP地址
    2)CNAME:Canonical Name,別名解析卒密,域名解析成另外一個域名
    3)MX記錄:Mail Exchange缀台,郵件服務(wù)器地址解析
    4)NS記錄:為某個域名指定DNS解析服務(wù)器

CDN工作機制

  • Content Delivery Network,內(nèi)容分布網(wǎng)絡(luò)哮奇,以緩存網(wǎng)站中的靜態(tài)數(shù)據(jù)為主膛腐,如CSS/JS/圖片/靜態(tài)頁面等數(shù)據(jù)睛约,目的是加速網(wǎng)頁數(shù)據(jù)內(nèi)容的下載速度。
  • 主要技術(shù)手段是鏡像服務(wù)器(Mirror)+ 高速緩存(Cache)+全局負載均衡DNS解析哲身。
  • 實現(xiàn)方式是在用戶和源服務(wù)器之間加入一層CDN緩存服務(wù)器辩涝;CDN對域名解析過程進行了調(diào)整,通過全局負載均衡DNS解析勘天,能夠根據(jù)地理位置信息怔揩,解析出最近的CDN服務(wù)器地址;用戶端向CDN緩存服務(wù)器發(fā)起請求脯丝,緩存服務(wù)器從源服務(wù)器得到內(nèi)容之后商膊,一方面在本地保存,以備之后使用宠进,另一方面把數(shù)據(jù)返回客戶端晕拆,完成數(shù)據(jù)服務(wù)過程。
  • 負載均衡
    1)鏈路負載均衡:如上面所提材蹬,通過DNS解析成不同的IP地址(就近原則)实幕,用戶根據(jù)這個IP地址訪問就近的目標服務(wù)器。
    2)集群負載均衡:硬件負載均衡(需要一臺昂貴的設(shè)備)堤器、軟件負載均衡(最普遍的方式昆庇,使用廉價的PC就可以搭建,缺點是一次訪問需要經(jīng)過多次代理服務(wù)器闸溃,增加網(wǎng)絡(luò)延時整吆;代理服務(wù)器的負載均衡算法如基于IP的輪詢算法)
    3)操作系統(tǒng)負載均衡:利用操作系統(tǒng)級別的軟中斷或硬中斷達到負載均衡,如設(shè)置多隊列網(wǎng)卡圈暗。

第二章 Java I/O的工作機制

Java的I/O類庫的基本架構(gòu)

  • 基于字節(jié)操作的IO操作接口:InputStream和OutputStream
  • 基于字符操作的IO操作接口:Reader和Writer
  • 基于磁盤操作的IO接口:File
  • 基于網(wǎng)絡(luò)操作的IO接口:Socket
  • 字節(jié)與字符的轉(zhuǎn)化接口
    1)InputStreamReader,從字節(jié)到字符的轉(zhuǎn)化橋梁裕膀,從InputStream到Reader的過程需要指定解碼字符集员串,否則采用操作系統(tǒng)默認的字符集,可能會出現(xiàn)亂碼問題昼扛。由StreamDecoder完成解碼過程寸齐。
    功能:從inputStream讀取字節(jié),并以字符格式抄谐,存儲到內(nèi)存渺鹦;inputStream可以是文件流,控制臺
    構(gòu)造器參數(shù):new InputStreamReader(InputStream in, String charSet)
    方法:read(char[] memory)
    2)OuputStreamWriter類完成從字符到字節(jié)的編碼過程蛹含,由StreamEncoder完成編碼過程毅厚。
    功能:將字符寫入到outputStream中;outputStream可以是文件流浦箱,控制臺
    構(gòu)造器參數(shù):OutputStreamWriter(OutputStream out, String charset)
    方法:write(String s)

磁盤I/O工作機制

  • 訪問文件的方式
    1)標準方式:read()/write() - 用戶地址空間(應(yīng)用緩存)- 內(nèi)核地址空間(高速頁緩存)- 物理磁盤
    2)直接I/O方式:應(yīng)用程序直接訪問磁盤數(shù)據(jù)吸耿,如數(shù)據(jù)庫管理系統(tǒng)
    3)同步訪問文件祠锣、異步訪問文件
    4)內(nèi)存映射方式:將內(nèi)核地址空間的某塊內(nèi)存和磁盤中的文件關(guān)聯(lián)起來,當訪問內(nèi)核地址空間的數(shù)據(jù)時咽安,轉(zhuǎn)換為訪問文件的某一段數(shù)據(jù)
  • Java訪問磁盤文件:FileInputStream對象作為InputStreamReader構(gòu)造器的參數(shù)
  • Java序列化技術(shù):將一個對象轉(zhuǎn)換成一串二進制表示的字節(jié)數(shù)組伴网,通過保存或轉(zhuǎn)移這些字節(jié)數(shù)據(jù)達到持久化的目的。對象需要持久化妆棒,必須繼承java.io.Serializable接口澡腾。反序列化時,需要有原始類作為模板糕珊,才能將這個對象還原动分。如果序列化的屬性是對象,則這個對象的也必須實現(xiàn)Serializable接口放接,否則會報錯刺啦。

網(wǎng)絡(luò)I/O工作機制

  • TCP狀態(tài)轉(zhuǎn)化
    1)Client端的狀態(tài):Closed,SYN-SENT纠脾,ESTABLISHED玛瘸,F(xiàn)IN-WAIT-1,F(xiàn)IN-WAIT-2苟蹈,TIME-WAIT
    2)Server端的狀態(tài):Closed糊渊,LISTEN,SYN-RECEIVED慧脱,ESTABLISHED渺绒,CLOSE-WAIT,LAST-ACK
    3)三次握手菱鸥,四次揮手
  • Java Socket的工作機制:上層應(yīng)用程序-Socket實例-TCP/UDP端口-IP
  • 數(shù)據(jù)傳輸:每個Socket實例都有一個inputStream和outPutStream宗兼,操作系統(tǒng)會為之分配一定大小的緩存區(qū),Client和Server都是通過這兩個對象來交換數(shù)據(jù)氮采。

NIO的工作方式

IO調(diào)優(yōu)

  • 磁盤IO優(yōu)化
  • TCP網(wǎng)絡(luò)參數(shù)調(diào)優(yōu)
  • 網(wǎng)絡(luò)IO優(yōu)化
    1)同步與異步:同步就是一個任務(wù)的完成需要依賴另外一個任務(wù)的完成主到,異步則沒有依賴關(guān)系
    2)阻塞與非阻塞:阻塞就是IO線程遇到慢的IO操作,仍然占用CPU時間片躯概;非阻塞就是遇到慢的IO操作登钥,釋放CPU占有權(quán),讓CPU執(zhí)行其它任務(wù)娶靡。

設(shè)計模式:適配器模式

  • 結(jié)構(gòu):Target目標接口牧牢,Adaptee適配接口,Adapter適配器類
  • 目的:Target接口和Adaptee接口往往有著類似功能的方法,但是方法的參數(shù)不一致结执;現(xiàn)想將Target接口適配到Adaptee接口度陆,即想在實現(xiàn)了Target接口的Adapter對象中調(diào)用Adaptee接口的方法。最終的好處是實現(xiàn)了功能的重用献幔。
  • 實現(xiàn):Adapter適配器類懂傀,實現(xiàn)了Target接口,同時繼承Adaptee源接口蜡感。
  • Java IO應(yīng)用舉例:InputStreamReader繼承了Reader抽象類蹬蚁,創(chuàng)建它們的對象必須在構(gòu)造器函數(shù)中傳入一個InputStream實例。這樣InputStreamReader就可以調(diào)用read()方法將字節(jié)讀入內(nèi)存郑兴,并以字符的形式存儲犀斋。InputStreamReader通過StreamDecoder類間接持有InputStream對象,并實現(xiàn)byte到char的編碼情连。

設(shè)計模式:裝飾器模式

  • 結(jié)構(gòu):Component(組件接口)叽粹、ConcreteComponent(組件實現(xiàn))、Decorator(裝飾器却舀,擁有Component實例)虫几、ConcreteDecorator(裝飾器實現(xiàn))
  • 舉例:Shape接口(draw方法)、Triangle/Circle/Rectangle(實現(xiàn)了draw方法)挽拔、ShapeDecorator(接收Component實例作為構(gòu)造器參數(shù))辆脸、RedShapeDecorator(繼承ShapeDecorator,重寫draw方法螃诅,除了調(diào)用super.draw方法啡氢,再調(diào)用red方法)
  • 實現(xiàn):Decorator類通過接收Component實例作為構(gòu)造器參數(shù),ConcreteDecorator繼承此類术裸,重寫方法倘是;
  • 目的:增強原有對象的功能、或者改變原有對象的處理方法而提升性能
  • Java IO應(yīng)用舉例:InputStream抽象類(組件)袭艺、FileInputStream類(組件實現(xiàn))搀崭、FilterInputStream類(裝飾器)、BufferdInputStream(裝飾器實現(xiàn))匹表。

第三章 Java Web的中文編碼問題

幾種常見的編碼格式

  • ASCII碼:128個字符门坷,一個字節(jié)表示
  • ISO-8859-1:擴展的ASCII碼宣鄙,256個字符袍镀,一個字節(jié)表示
  • GB2312:中文編碼字符集,682個符號冻晤,6763個漢字苇羡,英文:單字節(jié),中文:雙字節(jié)
  • GBK:漢字內(nèi)碼擴展規(guī)范鼻弧,擴展的GB2312編碼设江,21003個漢字锦茁。
  • UTF-16:左右字符都用兩個字節(jié)表示(16bit),定長表示
  • UTF-8:變長技術(shù)叉存,以0開頭的字節(jié)码俩,表示ASCII字符;以11開頭的字節(jié)歼捏,表示某個字符的首字節(jié)稿存,連續(xù)的1的個數(shù)表示這個字符的字節(jié)數(shù)。如110xxxxxx代表它是雙字節(jié)字符的首字節(jié)瞳秽;以10開頭瓣履,表示它不是首字節(jié),需要向前查找才能找到當前字符的首字節(jié)练俐。

在Java中需要編碼的場景

  • IO操作中存在的編碼
  • 內(nèi)存操作中存在的編碼

在Java Web中涉及的編解碼

  • URL的編解碼
  • Http Header的編解碼
  • POST表單的編解碼
  • Http Body的編解碼

在JS中的編碼問題

  • 外部引入JS文件
  • JS的URL編碼:escape(),encodeURI(),encodeURIComponent()
  • Java與JS的編解碼問題

第四章 Javac編譯原理

Javac編譯器的基本結(jié)構(gòu)

  • 詞法分析器
  • 語法分析器
  • 語義分析器
  • 代碼生成器

Javac工作原理分析

  • 詞法分析器:默認實現(xiàn)類是com.sun.tools.javac.parser.Scanner類袖迎,逐個讀取Java源文件的字符,解析出符合Java語言規(guī)范的Token序列:Token.PACKAGE腺晾,Token.IDENTIFIER燕锥,Token.SEMI,Token.PUBLIC丘喻,Token.CLASS脯宿,Token.LBRACE,Token.INT泉粉。
  • 語法分析器:將Token流組建成更加結(jié)構(gòu)化的語法樹连霉,也就是將一個個單詞組裝成一句話。
  • 語義分析器:在語法樹的基礎(chǔ)上再做一些處理嗡靡,添加默認的構(gòu)造函數(shù)跺撼,檢查變量在使用前是否已經(jīng)初始化,將一些常量進行合并處理讨彼,檢查操作變量類型是否匹配歉井,檢查所有語句是否可達。
  • 代碼生成器:com.sun.tools.javac.jvm.Gen遍歷語法樹哈误,生成最終的Java字節(jié)碼哩至。

設(shè)計模式:訪問者模式

  • 訪問者模式最大的優(yōu)點就是增加訪問者非常容易,如果要增加一個訪問者蜜自,只要新實現(xiàn)一個 Visitor 接口的類菩貌,從而達到數(shù)據(jù)對象與數(shù)據(jù)操作相分離的效果。如果不實用訪問者模式重荠,而又不想對不同的元素進行不同的操作箭阶,那么必定需要使用 if-else 和類型轉(zhuǎn)換,這使得代碼難以升級維護。
  • 具體事例:http://www.reibang.com/p/1f1049d0a0f4

第五章 深入class文件結(jié)構(gòu)

JVM指令集

  • 與類相關(guān)的指令:instanceof,new
  • 與方法相關(guān)的指令:invokeinterface,invokespecial(super.init,init,private),invokestatic,invokevirtual
  • 與類屬性相關(guān)的指令:getField,getstatic,putField,putstatic
  • 與棧操作相關(guān):dup(復(fù)制棧頂并入棧),pop(彈出棧頂元素),swap
  • 與本地變量(方法的局部變量)相關(guān):aload_0,dload_0,iload_0
  • 與運算相關(guān):dadd,isub
  • 與常量操作相關(guān):dconst_0, ldc(x)
  • 與Java控制指令相關(guān):dreturn,goto
  • 與Java數(shù)據(jù)類型轉(zhuǎn)換相關(guān):d2f,d2i
  • 與Java同步操作相關(guān):monitorenter,monitorexit
  • 與Java數(shù)組操作相關(guān): aaload,aastore,anewarray

class文件結(jié)構(gòu)

  • cafebabe + major version + minor version
  • 常量池:常量類型有UTF8,Integer,Class,String,Fieldref,Methodref,Interfaceref,NameAndType仇参,互相嵌套表示
  • 類描述信息:類訪問控制信息嘹叫,類名稱,父類名稱诈乒,接口數(shù)量罩扇,方法數(shù)量,成員變量數(shù)量
  • Method定義:方法訪問控制信息怕磨,方法名暮蹂,方法返回類型和方法參數(shù),方法實現(xiàn)癌压,LineNumberTable(字節(jié)碼指令與源碼行號對應(yīng))仰泻,LocalVariableTable(說明局部變量作用域)
  • Field定義:類似Method
  • 類屬性

第六章 深入分析ClassLoader工作機制

ClassLoader類結(jié)構(gòu)分析

  • defineClass:將byte字節(jié)流解析成JVM能夠識別的Class對象
  • findClass:找到類的字節(jié)碼,然后調(diào)用defineClass獲取類的Class對象滩届;用戶可覆蓋此方法實現(xiàn)自己的類加載規(guī)則
  • loadClass:獲取類的Class對象集侯,實現(xiàn)了雙親委派機制(findLoadedClass ? -> parent.loadClass ? -> bootstrap.loadClass ? -> findClass)
  • resolveClass:鏈接類;

ClassLoader的等級加載制度(雙親委派機制)

  • 啟動類加載器(Bootstrap ClassLoader):負責(zé)加載 %JAVA_HOME%/jre/lib/rt.jar
  • 擴展類加載器(Extension ClassLoader):負責(zé)加載 %JAVA_HOME%/jre/lib/ext/*.jar
  • 應(yīng)用程序類加載器(Application ClassLoader):或者稱為系統(tǒng)類加載器(System ClassLoader)帜消。它負責(zé)加載用戶類路徑(ClassPath)上所指定的類庫棠枉。
  • App ClassLoader繼承Extension ClassLoader,兩者均繼承URLClassLoader泡挺,URLClassLoader實現(xiàn)了ClassLoader接口
  • 隱式加載(類的繼承和引用)和顯示加載(代碼調(diào)用)
  • 雙親委派機制:如果一個類加載器收到了類加載的請求辈讶,它首先不會自己去嘗試加載這個類慌烧,而是把這個請求委派給父類加載器去完成顷歌,每一個層次的類加載器都是如此卤妒,因此所有的加載請求最終都應(yīng)該傳送到頂層的啟動類加載器中僻造,只有當父類加載器反饋自己無法完成這個加載請求時,子加載器才會嘗試自己去加載戏阅。
  • 雙親委派機制的優(yōu)點:避免同名類導(dǎo)致的混亂阅嘶。比如用戶自己編寫java.lang.Object類西轩,并放在程序的classpath中悬蔽,那系統(tǒng)將會出現(xiàn)多個不同的Object類扯躺,Java類型體系中最基礎(chǔ)的行為就無法保證。
  • 雙親委派機制的補缺機制:使用線程上下文加載器蝎困,可以讓父類加載器請求子類加載器去完成類加載的動作录语。用于解決上層的基礎(chǔ)類調(diào)用回用戶代碼的問題,比如JNDI接口調(diào)用JNDI服務(wù)實現(xiàn)代碼(即Service Provider Interface代碼)禾乘。JNDI服務(wù)可以使用線程上下文加載器去加載用戶的SPI代碼類澎埠。

如何加載class文件

  1. 加載:加載字節(jié)碼到內(nèi)存(findClass)
  2. 驗證:字節(jié)碼驗證
  3. 準備:類數(shù)據(jù)結(jié)構(gòu)準備和內(nèi)存分配
  4. 鏈接:符號表鏈接
  5. 初始化:靜態(tài)屬性,類屬性的初始化

常見的加載類錯誤

  • ClassNotFoundException:當JVM要加載指定類文件的字節(jié)碼到內(nèi)存時盖袭,沒有找到這個類文件對應(yīng)的字節(jié)碼失暂。檢查當前classpath路徑下有沒有指定的文件存在。當前classpath:this.getClass().getClassLoader().getResource("")
  • NoClassDefFoundError:某個使用類繼承或引用某個類鳄虱,這個類不存在弟塞,會導(dǎo)致拋出此類錯誤,同時也會拋出ClassNotFoundException拙已。

Tomcat類加載機制

  1. Tomcat目錄結(jié)構(gòu)
  • /common/*:類庫可被Tomcat和所有的Web應(yīng)用程序共同使用
  • /server/*:類庫可被Tomcat使用决记,對所有Web應(yīng)用程序不可見
  • /shared/*:類庫可被所有Web應(yīng)用程序使用,對Tomcat自己不可見
  • /WEB-INF/*:僅僅可以被此Web應(yīng)用程序使用倍踪,對Tomcat和其它Web應(yīng)用程序不可見
  1. Tomcat服務(wù)器的類加載架構(gòu)


  • Common ClassLoader:加載/common/*所有類
  • Catalina ClassLoader:加載/server/*所有類
  • Shared ClassLoader:加載/shared/*所有類
  • WebApp ClassLoader:可有多個WebApp ClassLoaer的實例系宫,每個實例代表一個Web應(yīng)用程序,加載對應(yīng)程序的/WEB-INF/*類建车。
  • JsperLoader:可有多個Jsp類加載器扩借,每個JSP文件對應(yīng)一個Jsp類加載器。這也是為什么jsp文件可實現(xiàn)熱加載缤至,不需要重啟服務(wù)器就可以替換類潮罪。通過卸載原先的JSP的類加載器和對應(yīng)的JSP文件,然后重新構(gòu)建一個新的JSP類加載器领斥,并加載新的JSP文件嫉到。
  • Tomcat破壞了雙親委派機制:WebApp ClassLoader直接加載自己應(yīng)用程序的/WEB-INF/*類。

實現(xiàn)類的熱部署

  • JVM判斷是否是同一個類:全類名+ClassLoader實例
  • 熱部署:卸載原ClassLoader實例月洛,創(chuàng)建新的ClassLoader實例對象何恶,并加載同名類。

第七章 JVM體系結(jié)構(gòu)與工作方式

JVM體系結(jié)構(gòu)

  • 類加載器:在JVM啟動時或者類運行時將需要的class(類的元數(shù)據(jù))加載到JVM的方法區(qū)嚼黔;每個被JVM裝載的類型都有一個對應(yīng)的java.lang.Class類的實例來表示該類型细层,該實例可以唯一表示被JVM裝載的class類,存放在Java的堆中唬涧。
  • 執(zhí)行引擎:基于棧的執(zhí)行引擎(hotspot)今艺,基于寄存器的執(zhí)行引擎(Dalvik);基于椌糇洌或寄存器指的是一個指令中的操作數(shù)是存在棧中虚缎,還是寄存器中。
  • 內(nèi)存區(qū):方法區(qū)(Perm)和堆钓株,Java棧和PC寄存器实牡,本地方法棧(C棧);其中方法區(qū)和堆是線程共享的轴合。
  • 本地方法調(diào)用:調(diào)用C或者C++實現(xiàn)的本地方法的代碼返回結(jié)果

JVM工作機制

  • JVM為何選擇基于棧的架構(gòu)
    1)JVM要設(shè)計成與平臺無關(guān)的创坞,而平臺無關(guān)性就是要保證在沒有或者很少寄存器的機器上同樣能正確的執(zhí)行;
    2)為了指令的緊湊性受葛,因為Java字節(jié)碼可能在網(wǎng)絡(luò)上傳輸题涨,需要字節(jié)碼指令盡量對齊偎谁,提高壓縮率。
  • 執(zhí)行引擎的架構(gòu)設(shè)計
    1)每當創(chuàng)建一個新的線程纲堵,JVM會為這個線程創(chuàng)建一個Java棧巡雨,同時會為這個線程分配一個PC寄存器;
    2)線程每調(diào)用一個新方法席函,會在棧上創(chuàng)建一個新的棧幀數(shù)據(jù)結(jié)構(gòu)铐望,每個棧幀會保留對應(yīng)方法的一些元信息,比如局部變量茂附、常量池的解析正蛙、正常方法返回和異常處理解析;其中關(guān)于常量池的解析营曼,需要訪問方法區(qū)乒验。

第八章 JVM內(nèi)存管理

物理內(nèi)存與虛擬內(nèi)存

  • 物理內(nèi)存:通常所說的RAM(隨機存儲器)
  • 虛擬內(nèi)存:虛擬內(nèi)存可以被映射到一段物理內(nèi)存、磁盤文件或者其他可以尋址的存儲上蒂阱。如Linux系統(tǒng)的swap區(qū)就是一塊磁盤存儲徊件,當系統(tǒng)的物理內(nèi)存不夠用時,可以將物理內(nèi)存中一部分釋放出來蒜危,供當前應(yīng)用程序使用虱痕。被釋放的物理內(nèi)存來自于長時間沒有操作的程序,被釋放空間中的數(shù)據(jù)保存在swap區(qū)辐赞。虛擬內(nèi)存的存在使得多個進程同時運行時可以共享物理內(nèi)存部翘、擴展了內(nèi)存空間。

內(nèi)核空間與用戶空間

  • 內(nèi)核空間主要指操作系統(tǒng)運行時所使用的用于程序調(diào)度响委、虛擬內(nèi)存的使用或者連接硬件資源等程序邏輯新思。用戶空間指用戶程序可以使用的空間。
  • 用戶程序不能直接訪問硬件資源赘风,如網(wǎng)絡(luò)連接等夹囚,可以調(diào)用操作系統(tǒng)提供的接口來實現(xiàn)。每一次系統(tǒng)調(diào)用都會存在兩個內(nèi)存空間的切換邀窃,通常的網(wǎng)絡(luò)傳輸也是一次系統(tǒng)調(diào)用荸哟,通過網(wǎng)絡(luò)傳輸?shù)臄?shù)據(jù)先是從內(nèi)核空間接收遠程主機的數(shù)據(jù),然后從內(nèi)核空間復(fù)制到用戶空間瞬捕。

Java中哪些組件需要使用內(nèi)存

  • Java堆:-Xms和-Xmx選項控制初始空間和最大空間鞍历。在JVM啟動時就一次性向操作系統(tǒng)申請完成。
  • 線程:每個線程創(chuàng)建時肪虎,JVM都會為它創(chuàng)建一個堆棧劣砍,通常在256k~756k之間。
  • 類和類加載器:類加載器本身扇救、類加載器加載的類存儲在方法區(qū)(Perm區(qū)刑枝,永久代)香嗓。如果Perm區(qū)不能對已經(jīng)失效的類做卸載,可能會導(dǎo)致Perm區(qū)的內(nèi)存泄漏装畅。類卸載條件:沒有類加載器實例的引用靠娱、該類加載器實例加載的所有類的Class實例和所有對象不再存活。這個條件相當苛刻洁灵,所以JVM的3個默認類加載器Bootstrap ClassLoader,ExtClassLoader掺出,AppClassLoader都不可能滿足這些條件徽千。
  • NIO:NIO使用java.io.ByteBuffer.allocateDirect()方法分配內(nèi)存,即NIO direct memort汤锨,使用的是本機內(nèi)存而不是Java堆上的內(nèi)存
  • JNI:Java Native Interface双抽,Java字節(jié)碼調(diào)用C/C++方法的技術(shù),使用native memory闲礼。比如文件操作牍汹、網(wǎng)絡(luò)IO操作或者其他系統(tǒng)調(diào)用,會依賴JNI代碼柬泽。

JVM內(nèi)存結(jié)構(gòu)

  • 堆:每個存儲在堆中的Java對象都是這個對象的類的一個副本慎菲。
  • 方法區(qū)(包括運行時常量池):存儲類的Class信息(即Class文件中的常量池、類信息锨并、Method定義露该、Field定義)、類的靜態(tài)變量
  • Java棧:每個線程都有自己的棧第煮,每個棧中含有多個棧幀(一個方法對應(yīng)一個棧幀)解幼。棧中主要存放一些基本類型的變量數(shù)據(jù)和對象應(yīng)用。
  • PC寄存器:記錄哪個線程執(zhí)行到哪條指令
  • 本地方法棧(C棧):為JVM運行Native方法準備的空間包警,包括JIT技術(shù)將Java方法重新編譯得到的Native Code撵摆。

JVM內(nèi)存分配策略

  • Java棧的內(nèi)存分配和線程綁定:一個線程的方法的調(diào)用和返回對應(yīng)于Java棧的棧幀的壓棧和出棧。
  • 棧中主要存放一些基本類型的變量數(shù)據(jù)和對象引用害晦。存取速度比堆快特铝,僅次于寄存器。缺點是棧中的數(shù)據(jù)大小和生存期必須是確定的壹瘟。
  • 棧中的slot可重用苟呐,因為局部變量有作用范圍,所以slot數(shù)可以小于局部變量數(shù)俐筋;slot在32位系統(tǒng)中為32bit牵素,一個long型或double型占2個slot;
  • 堆中存放所有類實例和數(shù)組澄者,由所有線程共享笆呆。對象的引用在棧中分配请琳。堆的優(yōu)勢是可以動態(tài)的分配內(nèi)存大小,生存期也不必告訴編譯器赠幕。缺點是運行時動態(tài)分配內(nèi)存導(dǎo)致的存取速度慢俄精。
  • 堆中的對象存儲信息
    1)對象頭:運行時數(shù)據(jù)和類型指針,運行時數(shù)據(jù)包括Hash碼榕堰,GC分代年齡竖慧,鎖狀態(tài)標志,線程鎖等
    2)實例數(shù)據(jù):代碼中定義的各類型字段逆屡,包括繼承父類的
    3)對齊填充:Hotspot內(nèi)存管理要求對象起始地址圾旨、對象大小、對象頭大小必須是8字節(jié)的整數(shù)倍魏蔗,否則需要填充
  • 堆主要用來存放對象砍的,棧主要用來運行程序。

JVM內(nèi)存回收策略

  • 靜態(tài)內(nèi)存分配與回收:靜態(tài)內(nèi)存指的是被編譯時就能夠確定需要的內(nèi)存空間莺治,主要包括類和方法中的原生數(shù)據(jù)類型(byte, short, int, long, char)和對象引用廓鞠。靜態(tài)內(nèi)存空間在程序被加載時一次性分配,代碼運行結(jié)束時收回谣旁。
  • 動態(tài)內(nèi)存分配與回收:動態(tài)內(nèi)存指的是在程序執(zhí)行時才知道要分配的存儲空間大小床佳,主要指實例對象。具體內(nèi)存大小只有在運行時榄审,確定對象的具體類型夕土,解析對應(yīng)的類才知道這個類中有哪些字段及其類型,才能分配空間瘟判。對象的回收也是不確定的怨绣,由垃圾收集器確定。
  • 如何檢測垃圾:只要某個對象不再被其他活動對象引用拷获,這個對象就可以被回收篮撑。具體來說就是是否能夠被根對象集合中任意一個對象引用。根對象集合包括:
    1)方法局部變量區(qū)對象引用匆瓜;
    2)Java棧中的對象引用赢笨;
    3)方法區(qū)常量池中的對象引用;
    4)本地方法中的對象引用驮吱;
  • Hotspot基于分代的垃圾回收算法
    1)分代:Young區(qū)茧妒,Old區(qū),Perm區(qū)左冬;Young區(qū)分為Eden區(qū)和Survivor區(qū)(From和To)桐筏;
    2)Eden區(qū)滿:內(nèi)存中新new的對象會被放入到Eden區(qū),若Eden區(qū)滿拇砰,將觸發(fā)minor GC梅忌,將Eden區(qū)和From區(qū)中仍活的對象移動至To區(qū)狰腌,F(xiàn)rom區(qū)和To區(qū)角色對調(diào),保證始終有個Survivor區(qū)是空的牧氮。如果Survivor區(qū)(To區(qū))存放不下這些對象琼腔,GC收集器會將這些對象直接放到Old區(qū)。
    3)Old區(qū)滿:Old區(qū)除了儲存上面所說的minor GC后survivor區(qū)存放不下的對象踱葛,還會存放Eden區(qū)過老的對象丹莲,即經(jīng)過多次minor GC仍然存活的對象。若old區(qū)滿尸诽,將會觸發(fā)Full GC甥材,回收整個堆內(nèi)存(包括Young區(qū),Survivor區(qū)逊谋,Perm區(qū))擂达。
    4)Perm區(qū)滿:Perm區(qū)存放的主要是類的Class對象土铺,如果加載的類對象過多胶滋,也可能會導(dǎo)致Perm區(qū)滿,也會觸發(fā)Full GC悲敷。
  • Hotspot提供的幾類垃圾收集器
    1)Serial GC:單線程串行回收究恤;JVM client模式下的默認選項;核心思想是上面的分代垃圾回收算法后德;
    額外細節(jié)有:在觸發(fā)Minor GC前會檢查之前每次Minor GC晉升到Old區(qū)的平均占用空間是否大于現(xiàn)在Old區(qū)的剩余空間部宿,如果是直接觸發(fā)Full GC。
    2)ParNewGC:新生代GC實現(xiàn)瓢湃,SerialGC的多線程版本理张;
    3)ParallelGC:JVM server模式下的默認選擇,吞吐量優(yōu)先的GC绵患,算法和Serial GC相似雾叭,特點是老年代和新生代GC并行進行,更加高效落蝙。通過ParallelOldGC參數(shù)可以選擇老年代的GC是否并行進行织狐;
    4)CMS GC:Concurrent Mark Sweep GC,基于標記-清除算法筏勒,盡量減少停頓時間移迫;存在碎片化問題,長時間運行則會導(dǎo)致Full GC管行。
  • GC參數(shù)集合
    Heap堆配置:-Xms厨埋,-Xmx,-Xmn捐顷,-XX:PermSize, -XX:MaxPermSize揽咕,-XX:SurvivorRatio
    GC組合參數(shù):-XX:+UseSerialGC悲酷,-XX:+UseParNewGC,-XX:+UseParallelGC亲善,-XX:+UseConcMarkSweepGC等
    GC算法參數(shù):-XX:MaxTenuringThreshold设易,默認為15,表示15次minorGC仍存活晉升到old區(qū)

內(nèi)存問題分析

  • GC日志分析蛹头,jstat工具
  • 堆快照文件分析:jmap -dump
  • JVM Crash日志分析

第九章 Servlet工作原理分析

Servlet容器

  • Servlet容器種類:Jetty顿肺,Tomcat等
  • Tomcat容器模型:Tomcat,Engine渣蜗,Host讼昆,Context浸赫,Wrapper
  • Servlet容器啟動過程:https://blog.csdn.net/dcrose/article/details/79729754
    以如下Tomcat的嵌入式例子描述Tomcat容器的啟動過程:
        Tomcat tomcat = new Tomcat();
        File appDir = new File(getBuildDirctory(), "webapps/examples");
        // Host host, String contextPath, String docBase
        tomcat.addWebapp(null, "/example", appDir.getAbsolutePath());
        // 啟動tomcat
        tomcat.start();
        // 調(diào)用Servlet 
        ByteChunk res = getUrl("http://localhost:" + getPort() + "/examples/servlets/servlet/HelloWorldExample");
        assert(res.toString().indexOf("<h1>Hello World!</h1>") > 0);

1)添加web應(yīng)用:tomcat.addWebapp
創(chuàng)建一個與此web應(yīng)用對應(yīng)的StandardContext容器,并將此容器添加到父容器Host中运敢;創(chuàng)建StanardContext容器之后传惠,會為此容器添加一個LifecycleListener對象:contextConfig,此contextConfig負責(zé)整個web應(yīng)用的配置解析工作愿汰。
2)啟動tomat容器:tomcat.start()
Tomcat的啟動邏輯是基于觀察者模式設(shè)計的衬廷,所有的容器都會繼承Lifecycle接口,它管理著容器的整個生命周期酗宋,所有容器的修改和狀態(tài)的改變都會由它通知已經(jīng)注冊的觀察者(Listener)蜕猫。所以Tomcat容器的啟動,會一層層激發(fā)子容器的LifecycleEvent翔烁。Tomcat啟動類的時序如下:Tomcat蹬屹,StandardServer,StandardService业筏,StandardEngine,StandardHost抛蚤,StandardContext,ContextConfig缀壤,Connector,Http11Protocal图呢,MapperListerner赴叹;我們需要重點關(guān)注StandardContext容器的啟動過程。
3)StandardContext容器的啟動過程
執(zhí)行ContextConfig的init方法:解析配置文件

解析默認的context.xml配置文件
解析默認的Host配置文件
解析默認的Context自身的配置文件
設(shè)置Context的DocBase

執(zhí)行StandardContext的startInternal方法:

創(chuàng)建讀取資源文件的對象
創(chuàng)建ClassLoader對象
設(shè)置應(yīng)用的工作目錄
啟動相關(guān)的輔助類摊欠,如logger些椒,realm,resources等
修改啟動狀態(tài)
子容器初始化
獲取ServletContext并設(shè)置必要的參數(shù)
初始化"load-on-startup > 0" 的Servlet

執(zhí)行ContextConfig的configureStart方法:完成Web應(yīng)用的初始化工作

解析各級web.xml文件石窑,包括globalWebXml,hostWebXml经宏,web應(yīng)用的web.xml。
然后將解析的對象屬性設(shè)置到Context容器中沪斟,包括創(chuàng)建Servlet對象、filter槽奕、listerner史翘。
Servlet對象被包裝成StandardWrapper并作為子容器添加到Context容器中。

Servlet工作原理

第10章 深入理解Session與Cookie

  • 什么是cookie
    1)當用戶通過Http訪問一個服務(wù)器,這個服務(wù)器會將一些Key/Value鍵值對返回給客戶端瀏覽器旗国;當用戶下次訪問這個服務(wù)器時,數(shù)據(jù)又被完整地帶回給服務(wù)器寿冕。
    2)cookie就是存儲在客戶端的一小段文本驼唱,cookie的作用是為了實現(xiàn)客戶端與服務(wù)器之間狀態(tài)的保持。
  • cookie應(yīng)用場景
    1)網(wǎng)站登錄纽窟,記錄用戶的登錄信息;
    2)網(wǎng)頁的個性化展示:當你訪問了某些網(wǎng)頁视搏,并且對網(wǎng)頁的一些設(shè)置進行修改,cookies就能跟蹤并記錄到這些修改筋遭,當你下一次訪問這個網(wǎng)頁的時候编饺,這個網(wǎng)頁會分析你電腦上的cookies透且,進而采取措施像你返回更符合你個性化的網(wǎng)頁;
    3)cookie能夠記錄用戶的瀏覽行為,目前大部分廣告的定位基礎(chǔ)也是基于cookies的锅论,比如你此前訪問了大量的健身類網(wǎng)站,cookies記錄了你的訪問行為耘纱,廣告主就能夠根據(jù)你的訪問行為,向你推送健身類的廣告员寇。
  • cookie屬性項
    1)NAME=VALUE:鍵值對
    2)Expires/Max-Age:過期時間
    3)Domain蝶锋,記錄此cookie是用戶訪問哪個domain時返回的信息
    4)Path,該Cookie是用戶訪問哪個路徑下生成的
  • cookie如何工作
    1)服務(wù)器端創(chuàng)建Cookie:Response.addCookie躯舔,Response.generateCookieString,Response.addHeader
    2)從客戶端獲取Cookie:當我們請求某個URL路徑時惜互,瀏覽器會根據(jù)這個URL路徑將符合條件的Cookie放在Request請求頭中傳回給服務(wù)端炒事,服務(wù)器端通過request.getCookies來獲取所有Cookie。
  • 使用Cookie的限制
    Cookie是Http頭中的一個字段睡扬,瀏覽器對Cookie的大小和數(shù)量有限制,一般為每個域名下的數(shù)量不超過50個阐枣,總大小不超過4kb甩鳄;
  • 什么是Session
    NAME為JSESSIONID的一個Cookie妙啃。解決了Cookie很多增加客戶端與服務(wù)器端的數(shù)據(jù)傳輸量的問題抑胎;同一個客戶端每次與服務(wù)器端交互時铭拧,不需要每次都傳回所有的Cookie值羽历,只要傳回一個ID炼团,這個ID是客戶端第一次訪問服務(wù)器時生成易桃,客戶端只要傳回這個ID就行了。如果客戶端禁用cookie造寝,可以將JSESSIONID作為path的參數(shù)傳遞到服務(wù)端。
  • Session如何工作
    1)request.getSession()方法觸發(fā):如果當前的SessionId還沒有對應(yīng)的HttpSession對象签赃,服務(wù)器端根據(jù)SessionID創(chuàng)建HttpSession對象,并將對象加入到sessions容器中保存括丁。只要這個HttpSession對象存在,用戶就可以根據(jù)SessionId來獲取這個對象,也就做到了對狀態(tài)的保持吐绵。Tomcat中session有效時間是60s己单,過期對象將被清除纹笼。
    2)Servlet容器關(guān)閉廷痘,會持久化保存容器中的Session對象到文件中蔓涧;重啟時再解析;
  • Cookie安全問題
    Cookie在瀏覽器端可訪問和修改笋额,不安全元暴;Session是將數(shù)據(jù)保存在服務(wù)端,較安全兄猩,更適合存儲用戶隱私和重要的數(shù)據(jù)鸠姨。
  • 分布式Session框架
    1)目的
    為了解決cookie的數(shù)量和大小限制,以及安全問題;
    2)功能
    session和cookie的統(tǒng)一配置和管理:服務(wù)訂閱服務(wù)器咱筛;
    容災(zāi)機制,服務(wù)器共享session和cookie:分布式緩存服務(wù)器龄砰;
    監(jiān)控和報警支持;
    跨域名Session和Cookie的共享:需要一個跳轉(zhuǎn)應(yīng)用,次應(yīng)用可被多個域名訪問
  • 相關(guān)介紹
    https://blog.csdn.net/hxfghgh/article/details/82840613
  • Cookie壓縮
    如果Cookie量非常大缤弦,可以考慮對cookie進行壓縮和編碼磁浇,可節(jié)省帶寬成本友题。
  • 表單重復(fù)提交問題
    這里的重復(fù)請求指的是一次表單請求呛凶,發(fā)了兩次(網(wǎng)速慢/惡意程序)。
    防止表單重復(fù)提交粒梦,服務(wù)端根據(jù)請求表單頁面,生成唯一token夫植,存儲到用戶Session中由捎,返回給客戶端心肪。用戶再次請求時,服務(wù)端驗證這個token和Session中的token是否一致。如果一致,說明沒有重復(fù)提交。
  • 多終端session統(tǒng)一
    1)多終端共享Session:分布式Session框架
    2)多終端登錄(app掃碼登錄):服務(wù)端設(shè)置標識位。

第11章 Tomcat的系統(tǒng)架構(gòu)與設(shè)計模式

  • tomcat的總體結(jié)構(gòu)
    參考:https://www.cnblogs.com/alimayun/p/10604532.html
    1)Connector組件:負責(zé)接收瀏覽器發(fā)過來的TCP連接請求,創(chuàng)建一個Request和Response對象分別用于和請求端交換數(shù)據(jù)。然后產(chǎn)生一個線程處理這個請求,處理這個請求的線程就是Container組件要做的事情榜跌。
    2)Container組件:Servlet容器
    Container是容器的父接口,由4個呈父子關(guān)系的子容器組件構(gòu)成,分別是Engine吱型、Host发皿、Context、Wrapper容器。
    3)Service服務(wù):Service用于關(guān)聯(lián)Connector和Container。一個Service可以包含多個Connector和一個Container键俱,這樣Connector在獲取客戶端的socket之后,交給對應(yīng)的Service嚷硫,由Service來找到對應(yīng)的Container检访,進而處理客戶端相關(guān)的請求。

  • Container容器具體介紹
    1)Engine容器:top容器仔掸,沒有父容器脆贵;
    2)Host容器:Engine的子容器,一個Host在Engine中代表一個虛擬主機起暮,這個虛擬主機的作用就是提供多個域名的服務(wù)卖氨。
    3)Context容器:Context表示應(yīng)用本身,管理這個應(yīng)用里面的所有Servlet實例鞋怀,它具備了Servelt運行的基本環(huán)境双泪。Servlet實例在Context中是以Wrapper出現(xiàn)的。
    4)Wrapper容器:Wrapper代表一個Servlet密似,它負責(zé)管理一個Servlet焙矛,包括Servlet的裝載、初始化残腌、執(zhí)行及資源回收村斟。

  • Tomcat中的設(shè)計模式
    1)門面設(shè)計模式
    優(yōu)點一:這種設(shè)計模式主要用在一個大的系統(tǒng)中有多個子系統(tǒng)時贫导,通過向用戶提供一個門戶,使得用戶只需要通過這個門戶來獲取他們想要的數(shù)據(jù)或者進行他們想要的操作蟆盹,而無需考慮這個大系統(tǒng)內(nèi)部的子系統(tǒng)構(gòu)成孩灯。
    https://zhuanlan.zhihu.com/p/98952478
    優(yōu)點二:門面設(shè)計模式是數(shù)據(jù)隔離的好辦法。多個子系統(tǒng)之間往往需要進行相互通信逾滥,但是每個子系統(tǒng)又不能將自己的內(nèi)部數(shù)據(jù)過多地暴露給其他系統(tǒng)峰档,所以為子系統(tǒng)設(shè)計一個門面,把別的系統(tǒng)感興趣的數(shù)據(jù)和操作封裝起來寨昙,通過這個門面來進行訪問讥巡。
    http://www.reibang.com/p/e74bbd9de09d
    2)觀察者設(shè)計模式
    通常也叫發(fā)布-訂閱模式,即事件監(jiān)聽機制舔哪,通常在某個事件發(fā)生的前后會觸發(fā)一些操作欢顷。Tomcat中控制容器組件生命周期的Lifecycle就是這種模式的體現(xiàn)祠挫。
    http://www.reibang.com/p/dcb676b58b7c
    3)命令設(shè)計模式
    命令模式的主要作用就是封裝命令因俐,把發(fā)出命令的責(zé)任和執(zhí)行命令的責(zé)任分開,也是一種功能的分工傀履。不同的模塊可以對同一個命令做出不同的解釋缆巧。Tomcat中Connector是通過命令模式調(diào)用Container的布持。
    https://blog.csdn.net/ZuoAnYinXiang/article/details/50457169
    4)責(zé)任鏈設(shè)計模式
    責(zé)任鏈模式就是很多對象由每個對象對其下家的引用而連接起來形成一條鏈,請求在這條鏈上傳遞盅蝗,直到鏈上的某個對象處理此請求鳖链,或者每個對象都可以處理請求,并傳給下家墩莫,直到最終鏈上每個對象都處理完。在Tomcat中這種設(shè)計模式幾乎被完全地使用了逞敷,Tomcat的容器設(shè)置就是責(zé)任鏈模式狂秦,從Engine到Host再到Context一直到Wrapper都通過一個鏈傳遞請求。
    http://www.reibang.com/p/58dc4580aa71
    https://blog.csdn.net/ZuoAnYinXiang/article/details/50457233

第12章 Jetty的工作原理解析

  • Jetty的基本架構(gòu)
    整個Jetty的核心由Server和Connector兩個組件構(gòu)成:
    1)Server組件:基于Handler容器工作推捐,類似于Tomcat的Container容器
    2)Connector組件:負責(zé)接收客戶端的連接請求裂问,并將請求分配給一個處理隊列去執(zhí)行
    整個Jetty所有組件的生命周期也是基于觀察者模式設(shè)計的,它和Tomcat的管理類似牛柒,基于LifeCycle接口工作堪簿。
    https://developer.ibm.com/zh/articles/j-lo-jetty/

  • Handler的體系結(jié)構(gòu)
    主要有兩種Handler類型:
    1)HandlerWrapper,它可以將Handler委托給另一個類去執(zhí)行皮壁;配合ScopeHandler椭更,可以攔截Handler的執(zhí)行,在Handler執(zhí)行的前后做另外一些事情蛾魄。
    2)HandlerCollection虑瀑,可將多個Handler組裝在一起湿滓,構(gòu)成一個Handler鏈,方便我們做擴展舌狗。

  • Jetty的啟動過程
    Jetty的入口是Server類叽奥;Jetty中所有組件都會繼承LifeCycle,所以Server的start方法就會調(diào)用所有已經(jīng)注冊到Server的組件痛侍,Server啟動其他組件的順序是:首先啟動設(shè)置到Server的Handler朝氓,然后依次啟動這個Handler的子Handler鏈,接著啟動注冊在Server的JMX的Mbean主届,最后啟動Connector赵哲,打開端口,接收客戶端請求岂膳。

  • 接收請求
    Jetty可以作為一個獨立的Servlet引擎誓竿,提供web服務(wù),基于Http協(xié)議工作谈截;也可以與其他Web應(yīng)用服務(wù)器集成筷屡,如JBoss服務(wù)器,基于AJP協(xié)議工作簸喂。
    1)基于HTTP協(xié)議工作:創(chuàng)建隊列線程池毙死、創(chuàng)建ServerSocket、創(chuàng)建監(jiān)聽線程
    2)基于AJP協(xié)議工作:Http的解析工作在外層的Apache或Nginx服務(wù)器已經(jīng)完成了喻鳄,后面都是基于AJP協(xié)議工作了扼倘。AJP處理請求相比于HTTP唯一的不同就是讀取到Socket數(shù)據(jù)包時如何來轉(zhuǎn)換這個數(shù)據(jù)包。
    3)基于NIO方式工作:Jetty的connector默認是基于NIO方式工作除呵。其實Jetty仍用一個線程來監(jiān)聽客戶端的連接請求再菊,把這個請求再注冊到Selector上,然后才以非阻塞的方式來執(zhí)行颜曾。

  • 處理請求
    Jetty收到一個請求時纠拔,就把這個請求交給在Server中注冊的代理Handler去執(zhí)行。接下來是Handler鏈的層層調(diào)用泛豪,基于責(zé)任鏈的設(shè)計模式稠诲。

  • 與JBoss集成
    1)Jetty可以基于AJP協(xié)議工作,在正常的企業(yè)級應(yīng)用中诡曙,Jetty作為一個Servlet引擎都是基于AJP工作的臀叙,所以它前面必然有一個服務(wù)器,通常情況下雨JBoss集成的可能性非常大价卤。
    2)JBoss是基于JMX架構(gòu)(http://www.reibang.com/p/8c5133cab858)的劝萤,所以只要符合JMX規(guī)范的系統(tǒng)都可以集成進來,Jetty當然也支持荠雕。

  • 與Tomcat的比較
    1)架構(gòu)比較
    從架構(gòu)上來說Jetty比Tomcat簡單稳其。Jetty是面向Handler的架構(gòu)驶赏,它的所有組件都是基于Handler來實現(xiàn)的;很容易被擴展和剪裁既鞠;Tomcat是以多級容器構(gòu)建起來的煤傍,相比之下,Tomcat臃腫很多嘱蛋,整體設(shè)計比較復(fù)雜
    2)性能比較
    在不同的場景下它們表現(xiàn)得各有差異蚯姆。Tomcat在處理少數(shù)非常繁忙的連接上更有優(yōu)勢,也就是說連接的生命周期如果短洒敏,Tomcat的總體性能更高龄恋。Jetty剛好相反,它可以同時處理大量連接而且可以長時間保持這些連接凶伙。例如一些Web聊天應(yīng)用非常適合使用Jetty做服務(wù)器郭毕,淘寶的Web旺旺就用Jetty作為Servlet引擎。
    3)特性比較
    Jetty對新的Servlet規(guī)范的支持速度更快函荣,因為它修改起來更加簡單显押。

第13章 Spring框架的設(shè)計理念與設(shè)計模式分析

  • Spring框架中的核心組件
    1)Bean
    位于org.springframework.beans包下,此包主要解決Bean的定義傻挂、Bean的創(chuàng)建乘碑、Bean的解析。
    Bean的創(chuàng)建是典型的工廠模式金拒,它的頂級接口是BeanFactory兽肤。
    Bean的解析主要是對Spring配置文件的解析。
    Bean的定義完整地描述了在Spring的配置文件中你定義的<bean/>節(jié)點中所有的信息绪抛。
    當Spring成功解析你定義的一個<bean/>節(jié)點后资铡,在Spring的內(nèi)部它就被轉(zhuǎn)化成BeanDefinition對象,以后所有的操作都是對這個對象進行幢码。
    2)Context
    位于org.spring.framework.context包下害驹,對Context來說就是要發(fā)現(xiàn)每個Bean之間的依賴關(guān)系,為它們建立并維護好這種依賴關(guān)系蛤育,所以Context就是一個Bean關(guān)系的集合,即Ioc容器葫松。實際上它就是給Spring提供一個運行時的環(huán)境瓦糕,用以保存各個對象的狀態(tài)。ApplicationContext是Context的頂級父類腋么。
    3)Core
    Core就是發(fā)現(xiàn)咕娄、建立、維護每個Bean之間的關(guān)系所需要的一系列工具珊擂,其中包含了很多關(guān)鍵類圣勒,一個重要的組成部分就是定義了資源的訪問方式费变。

  • Ioc容器如何工作
    Ioc容器實際上是Context組件結(jié)合其他兩個組件共同構(gòu)建了一個Bean關(guān)系網(wǎng)
    1)如何創(chuàng)建BeanFactory工廠
    2)如何創(chuàng)建Bean實例并構(gòu)建Bean的關(guān)系網(wǎng)
    3)Ioc容器的擴展點
    主要有BeanFactoryPostProcessor和BeanPostProcessor圣贸,它們分別在構(gòu)建BeanFactory和構(gòu)建Bean對象時調(diào)用挚歧。還有就是InitializingBean和DisposableBean,它們分別在Bean實例創(chuàng)建和銷毀時被調(diào)用吁峻。用戶可以在這些實現(xiàn)在這些接口中定義的方法滑负。
    4)Ioc容器如何為我所用
    ApplicationContext.xml就是Ioc容器默認的配置文件,Spring的所有特性功能都是基于Ioc容器工作的用含,如AOP矮慕。AOP的實現(xiàn)就是Spring本身實現(xiàn)了其擴展點達到了它想要的特性功能。
    https://www.cnblogs.com/linjiqin/archive/2013/11/04/3407126.html
    https://blog.csdn.net/qq_39530375/article/details/84533693

HelloInterface hello = new Hello();
InvocationHandler invocationHandler = new ProxyHandler(hello);
HelloInterface proxyHello = (HelloInterface) Proxy.newProxyInstance(hello.getClass().getClassLoader(), 
                                              hello.getClass().getInterfaces(), invocationHandler);
proxyHello.sayHello();

被代理對象:hello啄骇,實現(xiàn)了HelloInterface的sayHello()方法痴鳄;

代理對象:proxyHello,由Proxy.newProxyInstance方法構(gòu)造缸夹,接收參數(shù)為ClassLoader痪寻、被代理的接口、方法調(diào)用處理器明未;ClassLoader用于加載代理類槽华,通常和被代理類是同一個ClassLoader;被代理的接口即HelloInterface趟妥;方法調(diào)用處理器是一個實現(xiàn)了InvocationHandler接口的類猫态,代理對象調(diào)用被代理接口中的方法時,實際上就是由invocationHandler對象調(diào)用InvocationHandler接口中唯一的方法invoke()去處理披摄。

方法調(diào)用處理器:ProxyHandler類接收被代理對象作為構(gòu)造器的唯一參數(shù)亲雪,實現(xiàn)了InvocationHandler接口的invoke()方法。此方法就是所有被代理對象方法的實際執(zhí)行程序疚膊,通過反射調(diào)用被代理對象的方法义辕,并可以在方法調(diào)用前后加入我們的邏輯,這也是代理的最終目的寓盗。

public class ProxyHandler implements InvocationHandler{
    private Object object;
    public ProxyHandler(Object object){
        this.object = object;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before invoke "  + method.getName());
        method.invoke(object, args);
        System.out.println("After invoke " + method.getName());
        return null;
    }
}

2)Spring AOP如何實現(xiàn)
https://www.cnblogs.com/xuyatao/p/8485851.html
https://juejin.im/post/5af3bd6f518825673954bf22

  • 設(shè)計模式-代理模式
    代理模式就是給某一個對象創(chuàng)建一個代理對象灌砖,由這個代理對象控制原對象的引用,而創(chuàng)建這個代理對象后可以在調(diào)用原對象時增加一些額外的操作傀蚌。

  • 設(shè)計模式-策略模式
    通常是指完成某個操作可能有多種方法基显,這些方法各有千秋或者各有適用的場合。把各個操作方法都當做一個實現(xiàn)策略善炫,使用者可以根據(jù)需要選擇合適的策略撩幽。Spring中的策略模式的應(yīng)用有AOP代理的實現(xiàn),有JDK動態(tài)代理和CGLIB代理,JDK動態(tài)代理適用于目標類實現(xiàn)了接口窜醉,而CGLIB代理適用于目標類沒有實現(xiàn)任何接口宪萄。

第14章 Spring MVC的工作機制與設(shè)計模式

Spring MVC的總體設(shè)計

  • 搭建一個簡單的Spring MVC應(yīng)用
    在web.xml中配置一個DispatcherServlet,再定義一個dispacherServlet-servlet.xml配置文件榨惰,一個簡單的基于Spring MVC的應(yīng)用就創(chuàng)建成功拜英。
  • DispacherServlet類
    繼承HttpServlet,執(zhí)行Spring MVC的初始化工作:
    1)initMultipartResolver:用于處理文件上傳服務(wù)
    2)initLocaleResolver:處理應(yīng)用的國際化問題读串,即字符編碼問題聊记;
    3)initThemeResolver:定義用戶頁面樣式主題;
    4)initHandlerMapppings:定義用戶設(shè)置的請求映射關(guān)系恢暖,默認的HandlerMappings有BeanNameUrlHandlerMapping和DefaultAnnotationHandlerMapping排监。
    5)initHandlerAdapters:用于根據(jù)Handler的類型定義不同的處理規(guī)則,即用戶的業(yè)務(wù)處理邏輯杰捂。默認的有HttpRequestHandlerAdapter舆床,SimpleControllerHandlerAdapter,ThrowawayControllerHandlerAdapter和AnnotionMethodHandlerAdapter嫁佳。
    6)initHandlerExceptionResolvers:handler處理出錯挨队,統(tǒng)一交由這個Handler來處理。默認為SimpleMappingExceptionResolver蒿往。
    7)initRequestToViewNameTranslator:將指定的ViewName按照定義的RequestToViewNameTranslator替換成想要的格式盛垦,比如加入前綴或后綴。
    8)initViewResolvers:用于將View解析成頁面瓤漏。默認的解析策略有:根據(jù)JSP來解析(InternalResourceViewResolver),或者Velocity模板解析(VelocityViewResolver)蔬充,或者FreeMarker模板解析(FreeMarkerViewResolver)。
  • Spring MVC組件
    核心組件為:HandlerMapping榨呆,HandlerAdapter庸队,ViewResolver;
    其他組件:MultipartResolver彻消,ThemeResolver浅侨,HandlerExceptionResolver,LocaleResolver证膨,RequestToViewNameTranslator。
  • Spring MVC初始化流程
    https://blog.csdn.net/z695284766/article/details/80677434
  • 總體架構(gòu)圖


https://www.cnblogs.com/kanglijun/p/10553994.html

Control設(shè)計

  • Spring MVC的Control主要由HandlerMapping和HandlerAdapters兩個組件提供鼓黔。HandlerMapping負責(zé)映射用戶的URL和對應(yīng)的處理類央勒,HandlerMapping并沒有規(guī)定這個URL與應(yīng)用的處理類如何映射不见,接口中只定義了根據(jù)一個URL必須返回一個有HandlerExecutionChain代表的處理鏈,我們可以在這個處理鏈中添加任意的HandlerAdapters實例來處理這個URL對應(yīng)的請求崔步。

  • HandlerMapping初始化
    https://blog.csdn.net/qq_38410730/article/details/79507465

  • HandlerAdapter初始化
    HandlerAdapter可以幫助自定義各種Handler稳吮。Spring MVC中提供了三個典型的簡單HandlerAdapter實現(xiàn)類:
    1)HttpRequestHandlerAdapter:可以繼承HttpRequestHandler接口,所有Handler可以實現(xiàn)其void handleRequest(HttpServletRequest request, HttpServletResponse response)方法
    2)SimpleControllerHandlerAdapter:可以繼承Controller接口井濒,所有Handler可以實現(xiàn)其 ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)方法灶似,改方法返回ModelAndView對象,用于后續(xù)的模板渲染瑞你。
    3)SimpleServletHandlerAdapter:可以直接繼承Servlet接口酪惭,可以將一個Servlet作為一個Handler來處理這個請求。
    HandlerAdapter的初始化者甲,只是簡單創(chuàng)建一個HandlerAdapter對象春感,保存在DispatcherServlet的handlerAdapters集合中。當Spring MVC將某個URL對應(yīng)到某個handler時虏缸,在handlerAdapters集合中查詢哪個handlerAdapter對象支持這個handler鲫懒,返回該對象,并根據(jù)此對象的類型調(diào)用對應(yīng)的處理方法窥岩。

  • Control的調(diào)用邏輯
    整個Spring MVC的調(diào)用是從DispatcherServlet的doService方法開始的颂翼,在doService方法中會將ApplicationContext,localeResolver集歇,themeResolver等對象添加到request中供后面使用诲宇。接著調(diào)用doDispatch方法,這個方法是主要處理用戶請求的地方纺荧。
    Control的處理邏輯關(guān)鍵就是在DispatcherServlet的handlerMappings集合中根據(jù)請求的URL匹配HandlerMapping對象中的某個Handler输枯,匹配成功后將返回這個Handler的處理鏈HandlerExecutionChian對象桃熄,而在這個HandlerExecutionChain對象中將會包含用戶自定義的多個HandlerInterceptor對象。HandlerInterceptor對象用于在Handler處理邏輯前后增加額外的邏輯螟深。
    HandlerExecutionChain類的getHandler方法返回真正處理邏輯對象Handler,DispacherServlet會根據(jù)Handler對象在其handlerAdapters集合中匹配哪個HandlerAdapter實例來支持該Handler對象夹纫,然后執(zhí)行Handler對象的相應(yīng)方法。
    https://www.processon.com/special/template/5d8f13f4e4b0b2348043e0f5

Model設(shè)計(https://blog.csdn.net/en_joker/article/details/81903403)

ModelAndView對象是連接業(yè)務(wù)邏輯層與View展現(xiàn)層的橋梁月匣,對于Spring MVC來說它也是連接Handler和View的橋梁锄开。ModelAndView對象持有一個ModelMap對象和一個View對象,其中ModelMap對象就是執(zhí)行模板渲染時所需要的變量所在實例癣诱,如JSP通過request.getAtrribute(String)獲取的變量,Velocity中context.get(String)獲取的變量实抡。
參考:[https://blog.csdn.net/en_joker/article/details/81903403]

View設(shè)計

對Spring MVC的View模塊來說澜术,它由兩個組件支持,分別是RequestToViewNameTranslator和ViewResolver盒延。
1)RequestToViewNameTranslator:支持用戶自定義對ViewName的解析,如將請求的ViewName加上前綴或者后綴等计露。
2)ViewResolver:根據(jù)用戶請求的ViewName創(chuàng)建合適的模板引擎來渲染最終的頁面票罐,ViewResolver會根據(jù)ViewName創(chuàng)建一個View對象,調(diào)用View對象的void render(Map model, HttpServletRequest request, HttpServletResponse response)方法渲染頁面蚕礼。
上面提到的模板引擎有FreeMarkerViewResolver,InternalResourceViewResolver囤躁,VelocityViewResoler割以。其中InternalResourceViewResolver就是JSP的ViewResolver。
參考:https://blog.csdn.net/en_joker/article/details/81903797

設(shè)計模式-模板模式

模板模式的核心是消玄,大的邏輯已經(jīng)定義受扳,你要做的就是實現(xiàn)一些具體步驟勘高。

  • 模板模式結(jié)構(gòu)
    1)Abstract(抽象模板類):定義了完整的框架后,方法的調(diào)用順序通常已經(jīng)確定赖舟,但是會開放一些抽象的方法給子類去實現(xiàn)宾抓。
    *2)Concrete(具體模板實現(xiàn)類):實現(xiàn)抽象模板中定義的抽象方法,實現(xiàn)具體的功能劲腿,組成一個完整邏輯。
  • Spring MVC中的模板模式示例
    https://cloud.tencent.com/developer/article/1489850

第15章 深入分析iBatis框架之系統(tǒng)架構(gòu)與映射原理

iBatis框架主要的類層次結(jié)構(gòu)

  • iBatis主要完成以下兩件事情:
    1)根據(jù)JDBC規(guī)范建立與數(shù)據(jù)庫的連接
    2)通過反射打通Java對象與數(shù)據(jù)庫記錄之間相互轉(zhuǎn)化的關(guān)系花椭。
  • iBatis框架主要類層次結(jié)構(gòu)
    https://www.cnblogs.com/shaohz2014/archive/2014/06/16/3791131.html

iBatis框架的設(shè)計策略

  • SqlMap配置文件:配置CRUD等Statement語句矿辽。iBatis通過解析SqlMap配置文件得到所有Statement語句同時形成ParameterMap、ResultMap兩個對象和解析后SQL宾娜。
  • ParameterMap:按照Statement中參數(shù)的出現(xiàn)順序保存在Map集合中前塔,并根據(jù)配置解析出參數(shù)的Java數(shù)據(jù)類型食零。
  • ResultMap:包含了數(shù)據(jù)庫返回的查詢信息贰谣,用于填充返回對象;

iBatis框架的運行原理

  • iBatis運行過程中的主要執(zhí)行步驟
  • Spring調(diào)用iBatis執(zhí)行一個Statement的時序圖
  • 示例:https://www.cnblogs.com/cs-forget/p/6483895.html
    1)SqlMapConfig.xml:聲明所有SqlMapXXX.xml文件
    2)SqlMapXXX.xml:聲明跟XXX實體類相關(guān)的Statement,id屬性供DaoImpl中的方法匹配芝此;
    3)XXXDto實體類
    4)XXXDao接口和XXXDaoImpl實現(xiàn)類婚苹,XXXDaoImpl中實現(xiàn)增刪改查方法時關(guān)聯(lián)SqlMapXXX.xml定義的statement元素即可(根據(jù)id)膊升,傳入對應(yīng)的XXXDto實體類參數(shù)廓译。

iBatis對SQL語句的解析

  • 解析參數(shù):"#id:INTEGER",根據(jù)"#"分隔符構(gòu)建參數(shù)對象數(shù)組征绸,順序就是SQL中變量出現(xiàn)的順序;最終構(gòu)建的SQL中參數(shù)用?代替
  • 根據(jù)反射機制給參數(shù)賦值

數(shù)據(jù)庫字段映射到Java對象

  • ResultMap:根據(jù)ResultClass構(gòu)建對象缸榄,根據(jù)Map中的列名匹配對象中的屬性
  • 通過反射調(diào)用對象的setter()方法

設(shè)計模式解析之簡單工廠模式

第16章 Velocity工作原理解析

Velocity總體架構(gòu)

  • 代碼結(jié)構(gòu):Velocity主要分為App鹿驼、Context畜晰、Runtime和輔助Util。
    1)App:封裝接口块蚌,保留給使用者峭范;主要是Velocity和VelocityEngine
    2)Context:封裝了模板渲染需要的變量;比如MVC框架的ModelDataMap
    3)RuntimeInstance:為整個Velocity渲染提供一個單例模式甜害,是Velocity的一個門面尔店,封裝了渲染模板需要的所有接口

JJTree渲染過程分析

Velocity作為模板語言嚣州,其核心在于模板文件(.vm)的解析,構(gòu)建AST抽象語法樹沙庐。Velocity的解析器是通過JavaCC構(gòu)建的,JavaCC有一個擴充支持分析樹或AST抽象語法樹的生成铸抑,就是jjTree蒲赂。

  • Velocity的語法節(jié)點
    1)塊節(jié)點類型:表示一個代碼塊,主要由ASTReference若皱、ASTBlock、ASTExpression等組成互广。
    2)擴展節(jié)點類型:這些節(jié)點可以自己擴展兜辞,比如#foreach節(jié)點
    3)中間節(jié)點類型:位于樹的中間凶硅,它的渲染依賴子節(jié)點才能完成。ASTIfStatement和ASTSetDirective等
    4)葉子節(jié)點:ASTText和ASTTrue等氢妈,這種節(jié)點要么直接輸出值首量,要么寫到writer中鸭叙。
    Velocity讀取vm模板,根據(jù)JavaCC語法分析器將不同類型的節(jié)點按照上面幾個類型解析成一個完整的語法樹嗡善。每個節(jié)點的渲染都是執(zhí)行一個render方法。

  • Velocity中常用語法節(jié)點的渲染
    所謂渲染蜒程,即執(zhí)行的另一種行內(nèi)說法,意思就是執(zhí)行了一段代碼后,頁面會變成什么樣子帝洪。
    1)#set語法:創(chuàng)建一個Velocity變量
    2)#if、#elseif和#else語法:邏輯判斷節(jié)點
    3)#foreach語法
    4)#parse語法:對Velocity模板進行模塊化砰奕,可以將一些重復(fù)的模塊抽取出來單獨放在一個模板中,然后在其他模板中引入這個重用的模板提鸟。入#parse('head.vm')

  • Velocity的方法調(diào)用
    方法調(diào)用是通過"."來區(qū)分军援,set(name=person.name)中的person.name隱式調(diào)用了person.getName()方法。由于Velocity的方法調(diào)用是通過反射執(zhí)行的称勋,要考慮各種靈活的寫法胸哥,比較耗時。

事件處理機制

  • EventHandler是事件處理類的父接口
    1)ReferenceInsertionEventHandler:表示針對Velocity中變量的時間處理赡鲜,變量修改時觸發(fā)我們定義的事件,可以防止惡意JS代碼出現(xiàn)在頁面中
    2)NullSetEventHandler:對#set語法定義變量為空時的事件處理。
    3)MethodExceptionHandler:Velocity反射執(zhí)行調(diào)用方法報錯后的處理,可以捕獲異常惊畏,返回信息等。
    4)InvalidReferenceEventHandler:在解析$變量時沒有找到對應(yīng)對象時的事件處理透揣。
    5)IncludeEventHandler:在處理#include和#parse引入時提供了額外邏輯的入口放坏。

常用優(yōu)化技巧

  • 減少樹的總節(jié)點數(shù)量
  • 減少渲染耗時的節(jié)點數(shù)量

與JSP比較

  • JSP渲染機制
    1)JspServlet專門處理渲染JSP頁面镜廉,這個Servelt會根據(jù)請求的JSP文件名將這個JSP包裝成JspServletWrapper對象拓哟。
    2)JSP在執(zhí)行渲染時會被編譯成一個Java類,這個Java類實際上也是一個Servlet亮蒋。
    3)HttpJspBase類是所有Jsp編譯成的類的基類润努,HttpJspBase類的service方法會調(diào)用子類的_jspService方法倚聚,這個方法就是JSP頁面變異成Java類之后的所有頁面內(nèi)容所在的地方敞咧。
    4)DTCompiler類調(diào)用generateJava方法產(chǎn)生JSP對應(yīng)的Java文件,翻譯成Java類后仑荐,JDTCompiler再將這個類編譯成class文件猫牡,然后創(chuàng)建對象并初始化,接著調(diào)用這個類的service方法幸海,實際就是執(zhí)行_jspService方法肝集,完成渲染煌抒。
  • Velocity與JSP
    1)執(zhí)行方式不一樣:JSP是編譯執(zhí)行庇楞,Velocity是解釋執(zhí)行铆铆,如果JSP文件被修改,那么對象的Java類也會被重新編譯瞎抛,erVelocity卻不需要断凶,只是會重新生成一顆語法樹
    2)執(zhí)行效率不同:編譯執(zhí)行明顯好于解釋執(zhí)行
    3)需要的環(huán)境支持不一樣:JSP的執(zhí)行必須要有Servlet的運行環(huán)境;Velocity不只應(yīng)用在Servlet環(huán)境中。

設(shè)計模式之合成模式

合成模式又叫做部分整體模式,它通常把對象的關(guān)系映射到一棵樹中,利用樹的枝干和葉子節(jié)點來描述單個對象和組合對象玷禽,從而構(gòu)建統(tǒng)一的操作這些對象的接口,使得訪問對象的方式更加簡單呀打。
https://www.cnblogs.com/SamFlynn/p/4501227.html

設(shè)計模式之解釋器模式

就是將帶有一定文法的語句解析成特定的數(shù)據(jù)結(jié)構(gòu)矢赁,并提供一種解釋功能,使得能夠解釋這個語句贬丛。
https://www.cnblogs.com/adamjwh/p/10938852.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末撩银,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子豺憔,更是在濱河造成了極大的恐慌蜒蕾,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件焕阿,死亡現(xiàn)場離奇詭異咪啡,居然都是意外死亡,警方通過查閱死者的電腦和手機暮屡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門撤摸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人褒纲,你說我怎么就攤上這事准夷。” “怎么了莺掠?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵衫嵌,是天一觀的道長。 經(jīng)常有香客問我彻秆,道長楔绞,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任唇兑,我火速辦了婚禮酒朵,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘扎附。我一直安慰自己蔫耽,他們只是感情好,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布留夜。 她就那樣靜靜地躺著匙铡,像睡著了一般图甜。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上鳖眼,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天具则,我揣著相機與錄音,去河邊找鬼具帮。 笑死,一個胖子當著我的面吹牛低斋,可吹牛的內(nèi)容都是我干的蜂厅。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼膊畴,長吁一口氣:“原來是場噩夢啊……” “哼掘猿!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起唇跨,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤稠通,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后买猖,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體改橘,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年玉控,在試婚紗的時候發(fā)現(xiàn)自己被綠了飞主。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡高诺,死狀恐怖碌识,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情虱而,我是刑警寧澤筏餐,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站牡拇,受9級特大地震影響魁瞪,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜惠呼,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一佩番、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧罢杉,春花似錦趟畏、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽利朵。三九已至,卻和暖如春猎莲,著一層夾襖步出監(jiān)牢的瞬間绍弟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工著洼, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留樟遣,地道東北人。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓身笤,卻偏偏與公主長得像豹悬,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子液荸,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354