1.openfire的入口main文件在src/java 文件夾下的org.jivesoftware.openfire.starter包中。
2.運(yùn)行main函數(shù)之后openfire會(huì)調(diào)用start方法,首先是獲取到ClassLoader對(duì)象。那么什么是ClassLoader對(duì)象呢?
下面具體學(xué)習(xí)ClassLoader的知識(shí)。
首先ClassLoader作用是加載Class文件到j(luò)vm中,供程序使用,java程序可以動(dòng)態(tài)加載類定義,這個(gè)動(dòng)態(tài)加載的機(jī)制就是通過(guò)ClassLoader來(lái)實(shí)現(xiàn)的。
ClassLoader 是加載Class文件的(ExtClassLoader和AppClassLoader也在此時(shí)被加載),那么ClassLoader又被誰(shuí)加載呢?是一 個(gè)被不是java語(yǔ)言所編寫(xiě)的ClassLoader來(lái)加載的,這個(gè)ClassLoader就是bootstrapClassLoader(啟動(dòng)類加載 器)。這個(gè)加載器在jvm運(yùn)行的時(shí)候加載java核心的api以滿足java程序最基本的需求。其中包括用戶定義的ClassLoader,用戶定義的 ClassLoader就是通過(guò)程序創(chuàng)建的ClassLoader,那么也有非程序員創(chuàng)建的ClassLoader,就是jvm自己提供的吧(這句是自己 理解的)。用戶自定義的ClassLoader有ExtClassLoader,ExtClassLoader加載java的擴(kuò)展的api,也就是 /lib/ext中的類。用戶自定義的ClassLoader還有AppClassLoader,AppClassLoader用戶機(jī)器上的 CLASSPATH設(shè)置目錄中的Class的,通常在沒(méi)有指定ClassLoader的情況下,程序自定義的類由AppClassLoader加載
ClassLoader 的加載模式:雙親委托模式進(jìn)行加載。該模式的原理是:某個(gè)自定義的ClassLoader加載Class的時(shí)候都會(huì)先委托他的parnet ClassLoader加載該Class,當(dāng)parent ClassLoader加載失敗,再由當(dāng)前的ClassLoader加載該Class,但是如果該ClassLoader的parent ClassLoader為null那么該ClassLoader的parent就是bootstrapClassLoader。
使用雙親委托模式的優(yōu)點(diǎn)是:
第一:避免重復(fù)加載,當(dāng)父親已經(jīng)加載了該類,那么子ClassLoader就沒(méi)有必要加載該class了。
第二:安全因素。
3.獲取當(dāng)前類的類類加載器的方法:
[java]
view plaincopy<span style="background-color:rgb(51,153,102)">public ClassLoader findParentClassLoader(){
//獲取父類加載器
ClassLoader parent = Thread.currentThread().getContextClassLoader();
if(parent==null){
parent = this.getClass().getClassLoader();
if(parent==null){
parent = ClassLoader.getSystemClassLoader();
}
}
return parent;
}</span>
4.類加載器的種類:
bootstrap class Loader(引導(dǎo)類加載器) 用來(lái)加載java的核心類庫(kù)
extensions class loader(擴(kuò)展類加載器) 用來(lái)加載java的擴(kuò)展庫(kù)Java 虛擬機(jī)的實(shí)現(xiàn)會(huì)提供一個(gè)擴(kuò)展庫(kù)目錄。該類加載器在此目錄里面查找并加載 Java 類[ExtClassLoader]
系統(tǒng)類加載器(system class loader)Java 應(yīng)用的類都是由它來(lái)完成加載的。可以通過(guò) ClassLoader.getSystemClassLoader() 來(lái)獲取它[AppClassLoader]
上一節(jié)主要學(xué)習(xí)了jvm的類加載器,這節(jié)繼續(xù)進(jìn)行,從org.jivesoftware.openfire.starter.ServerStarter文件的第72行進(jìn)行解讀。
System.getProperty("openfire.lib.dir");
上面這句話是什么意思呢,根據(jù)字面意思理解應(yīng)該是獲取到當(dāng)前項(xiàng)目也就是openfire的lib路徑
繼續(xù)往下讀,如果存放lib的路徑不存在那么就創(chuàng)建一個(gè)存放lib的文件夾
同樣的通過(guò)這個(gè)方法可以獲取到其他的屬性 如下列表
java.version
Java 運(yùn)行時(shí)環(huán)境版本
java.vendor
Java 運(yùn)行時(shí)環(huán)境供應(yīng)商
java.vendor.url
Java 供應(yīng)商的 URL
java.home
Java 安裝目錄
java.vm.specification.version
Java 虛擬機(jī)規(guī)范版本
java.vm.specification.vendor
Java 虛擬機(jī)規(guī)范供應(yīng)商
java.vm.specification.name
Java 虛擬機(jī)規(guī)范名稱
java.vm.version
Java 虛擬機(jī)實(shí)現(xiàn)版本
java.vm.vendor
Java 虛擬機(jī)實(shí)現(xiàn)供應(yīng)商
java.vm.name
Java 虛擬機(jī)實(shí)現(xiàn)名稱
java.specification.version
Java 運(yùn)行時(shí)環(huán)境規(guī)范版本
java.specification.vendor
Java 運(yùn)行時(shí)環(huán)境規(guī)范供應(yīng)商
java.specification.name
Java 運(yùn)行時(shí)環(huán)境規(guī)范名稱
java.class.version
Java 類格式版本號(hào)
java.class.path
Java 類路徑
java.library.path
加載庫(kù)時(shí)搜索的路徑列表
java.io.tmpdir
默認(rèn)的臨時(shí)文件路徑
java.compiler
要使用的 JIT 編譯器的名稱
java.ext.dirs
一個(gè)或多個(gè)擴(kuò)展目錄的路徑
os.name
操作系統(tǒng)的名稱
os.arch
操作系統(tǒng)的架構(gòu)
os.version
操作系統(tǒng)的版本
file.separator
文件分隔符(在 UNIX 系統(tǒng)中是“/”)
path.separator
路徑分隔符(在 UNIX 系統(tǒng)中是“:”)
line.separator
行分隔符(在 UNIX 系統(tǒng)中是“/n”)
user.name
用戶的賬戶名稱
user.home
用戶的主目錄
user.dir
用戶的當(dāng)前工作目錄
上一節(jié)我們閱讀到了org.jivesoftware.openfire.starter.ServerStarter文件中的第90行,這節(jié)繼續(xù)。
第90行調(diào)用unpackArchives(libDir, true);方法。
通過(guò)閱讀該方法的英文注釋大概意思是:轉(zhuǎn)換文件夾中的一些包文件為一個(gè)標(biāo)準(zhǔn)的jar文件,在轉(zhuǎn)換jar文件的同時(shí)每個(gè)被轉(zhuǎn)的包文件就會(huì)被刪除,如果包文件不存在,那么就什么都不做。
過(guò)濾文件
帶著這句話我們進(jìn)行閱讀。該方法傳入了2個(gè)參數(shù),第一個(gè)是一個(gè)lib文件夾,第二個(gè)參數(shù)是個(gè)boolean值true。
[java]
view plaincopyFile [] packedFiles = libDir.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.endsWith(".pack");
}
});
if (packedFiles == null) {
// Do nothing since no .pack files were found
return;
}
以上代碼是unpackArchives方法中的146到155行的代碼。
上面的第一句話我們大家應(yīng)該很熟悉,沒(méi)錯(cuò),這句話我也經(jīng)常用,但是我經(jīng)常用不帶參數(shù)的方法也就是一般這樣用,
File [] packedFiles = libDir.listFiles();
這樣是獲取到一個(gè)文件夾下的所有的文件。
而帶參數(shù)的根據(jù)字面意思大概是過(guò)濾文件名稱的意思,就是過(guò)濾一定規(guī)則的文件,而不是顯示所有的文件,
過(guò)濾用FilenameFilter這個(gè)接口,一般我們用接口都是通過(guò)繼承的方法來(lái)使用,但是我們現(xiàn)在通過(guò)new的方式來(lái)使用,其實(shí)這種用法還是蠻多的,比如很多注冊(cè)事件== 很多地方都大量運(yùn)用了該方法。
但是new接口的時(shí)候我們會(huì)發(fā)現(xiàn)我們就要實(shí)現(xiàn)里面所有的方法,少一個(gè)方法都不可以。因?yàn)镕ilenameFilter接口只有一個(gè)方法accept所以我們?cè)趎ew的同時(shí)就會(huì)實(shí)現(xiàn)該方法,我們通過(guò)該方法直接過(guò)濾以某種后綴名的文件就可以了現(xiàn)在我們要列出.pack類型的文件所以我們應(yīng)該寫(xiě) return name.endsWith(".pack");就可以獲取到了。很方便吧。
通過(guò)以上代碼我們學(xué)習(xí)一個(gè)知識(shí)點(diǎn),那就是獲取某個(gè)文件夾下的某種格式的文件列表應(yīng)該用FilenameFilter來(lái)實(shí)現(xiàn),實(shí)現(xiàn)方法是一下代碼
[java]
view plaincopyFile [] packedFiles = libDir.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.endsWith(".pack");
}
});
如果沒(méi)有獲取到.pack類型的文件那么什么都不做,直接返回。
具體實(shí)現(xiàn)把.pack的文件轉(zhuǎn)換為jar文件
上面我們獲取到了.pack的文件數(shù)據(jù),然后開(kāi)始遍歷該數(shù)組,把每個(gè).pack文件轉(zhuǎn)為jar文件。
關(guān)鍵代碼如下
[java]
view plaincopy<span style="background-color:rgb(153,204,0)">InputStream in = new BufferedInputStream(new FileInputStream(packedFile));
JarOutputStream out = new JarOutputStream(new BufferedOutputStream(
new FileOutputStream(new File(libDir, jarName))));
Pack200.Unpacker unpacker = Pack200.newUnpacker();
// Print something so the user knows something is happening.
if (printStatus) {
System.out.print(".");
}
// Call the unpacker
unpacker.unpack(in, out);
in.close();
out.close();
packedFile.delete();
unpacked = true;</span>
以上的packedFile是遍歷每個(gè).pack的文件。就這樣把.pack文件轉(zhuǎn)為了jar文件。
第三節(jié)中我們閱讀了org.jivesoftware.openfire.starter.ServerStarter文件到91行,繼續(xù)吧!
這節(jié)我們跳過(guò)108行之前的從108行開(kāi)始學(xué)習(xí),91行到107行相對(duì)比較簡(jiǎn)單。
從第108行到113行主要做了2件事情
第一:加載系統(tǒng)用到的jar包跟zip包到classpath中
第二:通過(guò)反射加載org.jivesoftware.openfire.XMPPServer類文件。
一:那么如何加載文件到classpath中呢
openfire用什么加載文件到classpath中:openfire中用org.jivesoftware.openfire.starter.JiveClassLoader加載文件到classpath中(該類是繼承了URLClassLoader)
openfire加載文件到classpath的方法:
首先找出jar跟zip類型的文件,代碼如下
1
2
3
4
5
6
7
8
9
10
11
12
13
File[] jars = libDir.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {?
boolean accept = false;
String smallName = name.toLowerCase();
if (smallName.endsWith(".jar")) {
accept = true;
}
else if (smallName.endsWith(".zip")) {
accept = true;
}
return accept;
}
});
然后調(diào)用父類URLClassLoader的addURL方法加載文件到classpath中,代碼如下
1
2
3
4
5
for (int i = 0; i < jars.length; i++) {
if (jars[i].isFile()) {
addURL(jars[i].toURI().toURL());
}
}
二:如何通過(guò)反射加載文件?
1
2
3
4
5
6
7
8
//創(chuàng)建了一個(gè)自定義的類加載器,該類加載器同時(shí)加載了所需要的jar包
ClassLoader loader = new JiveClassLoader(parent, libDir);
//設(shè)置自定義的類加載器為當(dāng)前線程的類加載器
Thread.currentThread().setContextClassLoader(loader);
//利用當(dāng)前線程的類加載器加載類文件,注意:這里需要帶包名寫(xiě)全,不然項(xiàng)目中不通包中出現(xiàn)相同類文件就可能出現(xiàn)找不到的情況
Class containerClass = loader.loadClass(
"org.jivesoftware.openfire.XMPPServer");
containerClass.newInstance();
到這里org.jivesoftware.openfire.starter.ServerStarter文件都閱讀完畢,ServerStarter中主要做了如下幾件事情:
獲取classpath路徑
把.pack文件轉(zhuǎn)換為jar文件
獲取當(dāng)前線程的類加載器
創(chuàng)建自定義類加載器,并加載jar文件跟zip文件到classpath中
利用自定義的類加載器啟動(dòng)org.jivesoftware.openfire.XMPPServer類文件