1.openfire的入口main檔案在src/java 檔案夾下的org.jivesoftware.openfire.starter包中。
2.運作main函數之後openfire會調用start方法,首先是擷取到ClassLoader對象。那麼什麼是ClassLoader對象呢?
下面具體學習ClassLoader的知識。
- 首先ClassLoader作用是加載Class檔案到jvm中,供程式使用,java程式可以動态加載類定義,這個動态加載的機制就是通過ClassLoader來實作的。
- ClassLoader 是加載Class檔案的(ExtClassLoader和AppClassLoader也在此時被加載),那麼ClassLoader又被誰加載呢?是一 個被不是java語言所編寫的ClassLoader來加載的,這個ClassLoader就是bootstrapClassLoader(啟動類加載 器)。這個加載器在jvm運作的時候加載java核心的api以滿足java程式最基本的需求。其中包括使用者定義的ClassLoader,使用者定義的 ClassLoader就是通過程式建立的ClassLoader,那麼也有非程式員建立的ClassLoader,就是jvm自己提供的吧(這句是自己 了解的)。使用者自定義的ClassLoader有ExtClassLoader,ExtClassLoader加載java的擴充的api,也就是 /lib/ext中的類。使用者自定義的ClassLoader還有AppClassLoader,AppClassLoader使用者機器上的 CLASSPATH設定目錄中的Class的,通常在沒有指定ClassLoader的情況下,程式自定義的類由AppClassLoader加載
- ClassLoader 的加載模式:雙親委托模式進行加載。該模式的原理是:某個自定義的ClassLoader加載Class的時候都會先委托他的parnet ClassLoader加載該Class,當parent ClassLoader加載失敗,再由目前的ClassLoader加載該Class,但是如果該ClassLoader的parent ClassLoader為null那麼該ClassLoader的parent就是bootstrapClassLoader。
-
使用雙親委托模式的優點是:
第一:避免重複加載,當父親已經加載了該類,那麼子ClassLoader就沒有必要加載該class了。
第二:安全因素。
3.擷取目前類的類類加載器的方法:
public ClassLoader findParentClassLoader(){
//擷取父類加載器
ClassLoader parent = Thread.currentThread().getContextClassLoader();
if(parent==null){
parent = this.getClass().getClassLoader();
if(parent==null){
parent = ClassLoader.getSystemClassLoader();
}
}
return parent;
}
4.類加載器的種類:
- bootstrap class Loader(引導類加載器) 用來加載java的核心類庫
- extensions class loader(擴充類加載器) 用來加載java的擴充庫Java 虛拟機的實作會提供一個擴充庫目錄。該類加載器在此目錄裡面查找并加載 Java 類[ExtClassLoader]
- 系統類加載器(system class loader)Java 應用的類都是由它來完成加載的。可以通過 ClassLoader.getSystemClassLoader() 來擷取它[AppClassLoader]
上一節主要學習了jvm的類加載器,這節繼續進行,從org.jivesoftware.openfire.starter.ServerStarter檔案的第72行進行解讀。
System.getProperty("openfire.lib.dir");
上面這句話是什麼意思呢,根據字面意思了解應該是擷取到目前項目也就是openfire的lib路徑
繼續往下讀,如果存放lib的路徑不存在那麼就建立一個存放lib的檔案夾
同樣的通過這個方法可以擷取到其他的屬性 如下清單
java.version | Java 運作時環境版本 |
java.vendor | Java 運作時環境供應商 |
java.vendor.url | Java 供應商的 URL |
java.home | Java 安裝目錄 |
java.vm.specification.version | Java 虛拟機規範版本 |
java.vm.specification.vendor | Java 虛拟機規範供應商 |
java.vm.specification.name | Java 虛拟機規範名稱 |
java.vm.version | Java 虛拟機實作版本 |
java.vm.vendor | Java 虛拟機實作供應商 |
java.vm.name | Java 虛拟機實作名稱 |
java.specification.version | Java 運作時環境規範版本 |
java.specification.vendor | Java 運作時環境規範供應商 |
java.specification.name | Java 運作時環境規範名稱 |
java.class.version | Java 類格式版本号 |
java.class.path | Java 類路徑 |
java.library.path | 加載庫時搜尋的路徑清單 |
java.io.tmpdir | 預設的臨時檔案路徑 |
java.compiler | 要使用的 JIT 編譯器的名稱 |
java.ext.dirs | 一個或多個擴充目錄的路徑 |
os.name | 作業系統的名稱 |
os.arch | 作業系統的架構 |
os.version | 作業系統的版本 |
file.separator | 檔案分隔符(在 UNIX 系統中是“/”) |
path.separator | 路徑分隔符(在 UNIX 系統中是“:”) |
line.separator | 行分隔符(在 UNIX 系統中是“/n”) |
user.name | 使用者的賬戶名稱 |
user.home | 使用者的主目錄 |
user.dir | 使用者的目前工作目錄 |
上一節我們閱讀到了org.jivesoftware.openfire.starter.ServerStarter檔案中的第90行,這節繼續。
第90行調用unpackArchives(libDir, true);方法。
通過閱讀該方法的英文注釋大概意思是:轉換檔案夾中的一些封包件為一個标準的jar檔案,在轉換jar檔案的同時每個被轉的封包件就會被删除,如果封包件不存在,那麼就什麼都不做。
- 過濾檔案
帶着這句話我們進行閱讀。該方法傳入了2個參數,第一個是一個lib檔案夾,第二個參數是個boolean值true。
File [] 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行的代碼。
上面的第一句話我們大家應該很熟悉,沒錯,這句話我也經常用,但是我經常用不帶參數的方法也就是一般這樣用,
File [] packedFiles = libDir.listFiles();
這樣是擷取到一個檔案夾下的所有的檔案。
而帶參數的根據字面意思大概是過濾檔案名稱的意思,就是過濾一定規則的檔案,而不是顯示所有的檔案,
過濾用
FilenameFilter
這個接口,一般我們用接口都是通過繼承的方法來使用,但是我們現在通過new的方式來使用,其實這種用法還是蠻多的,比如很多注冊事件== 很多地方都大量運用了該方法。
但是new接口的時候我們會發現我們就要實作裡面所有的方法,少一個方法都不可以。因為
FilenameFilter
接口隻有一個方法
accept
是以我們在new的同時就會實作該方法,我們通過該方法直接過濾以某種字尾名的檔案就可以了現在我們要列出.pack類型的檔案是以我們應該寫
return
name.endsWith(
".pack"
);
就可以擷取到了。很友善吧。
通過以上代碼我們學習一個知識點,那就是擷取某個檔案夾下的某種格式的檔案清單應該用
FilenameFilter
來實作,實作方法是一下代碼
File [] packedFiles = libDir.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.endsWith(".pack");
}
});
如果沒有擷取到.pack類型的檔案那麼什麼都不做,直接傳回。
-
具體實作把.pack的檔案轉換為jar檔案
上面我們擷取到了.pack的檔案資料,然後開始周遊該數組,把每個.pack檔案轉為jar檔案。
關鍵代碼如下
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;
以上的
packedFile
是周遊每個.pack的檔案。就這樣把.pack檔案轉為了jar檔案。
第三節中我們閱讀了org.jivesoftware.openfire.starter.ServerStarter檔案到91行,繼續吧!
這節我們跳過108行之前的從108行開始學習,91行到107行相對比較簡單。
從第108行到113行主要做了2件事情
第一:加載系統用到的jar包跟zip包到classpath中
第二:通過反射加載org.jivesoftware.openfire.XMPPServer類檔案。
一:那麼如何加載檔案到classpath中呢
- openfire用什麼加載檔案到classpath中:openfire中用org.jivesoftware.openfire.starter.JiveClassLoader加載檔案到classpath中(該類是繼承了URLClassLoader)
-
openfire加載檔案到classpath的方法:
首先找出jar跟zip類型的檔案,代碼如下
然後調用父類URLClassLoader的addURL方法加載檔案到classpath中,代碼如下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;
}
});
1 2 3 4 5 for
(
int
i =
; i < jars.length; i++) {
if
(jars[i].isFile()) {
addURL(jars[i].toURI().toURL());
}
}
二:如何通過反射加載檔案
1 2 3 4 5 6 7 8 | |
到這裡org.jivesoftware.openfire.starter.ServerStarter檔案都閱讀完畢,ServerStarter中主要做了如下幾件事情:
- 擷取classpath路徑
- 把.pack檔案轉換為jar檔案
- 擷取目前線程的類加載器
- 建立自定義類加載器,并加載jar檔案跟zip檔案到classpath中
- 利用自定義的類加載器啟動org.jivesoftware.openfire.XMPPServer類檔案