引用
classloader機制
- 如下圖所示,java的classloader是雙親委派機制。會首先從父classloader加載指定的class,如果加載不到才會從子classloader中加載。

- 主要這里的圖片主要用於體現classloader的父子關系,實際上實現時並不一定存在繼承關系。比如
AppClassLoader的父classLoader是ExtClassLoader,但是實際實現時兩者都是繼承自URLClassLoader的。
自定義classloader
Animal類&包結構

- 包結構如圖:

測試入口

- 輸出結果如圖所示:

思考1
- 我們定義的classloader真的被運行了嗎?
- 實際上如果打一些日志或者是debug進去看下,會發現我們重寫的方法並沒有被執行,這是java classloader的委派機制搞的鬼,我們可以發現定制的
TinglangClassLoader的父classloader為URLClassLoader,在URLClassLoader中就已經找到了Animal類了。 - 所以這里可以重寫
loadClass方法來保證我們的classLoader被執行。
思考2
- 我們將代碼做一些變種,如下圖所示:

- 執行結果直接拋異常了

- 這是由於
Animal類來源於兩個不同的類加載器。如果只是需要執行Animal中的say方法的話,直接反射調用即可,如下圖所示: 
思考3
- 我們再將代碼做一下變化,如下圖所示,
Animal作為一個普通內部類來實現,如下圖所示: 
- 可以看到代碼已經提示無法實例化內部類了,這個是由於要實例化非靜態的內部類對象,必須要先實例化外部類的對象,可以采用下面的方法來解決:

- ps:這里內部類有
public修飾符~
思考4
- tomcat也是定義了自己的classloader,那么為啥不用jvm提供的classloader呢?
- 結合相關的資料主要是三個方面的目的:
- webapp隔離:由於各個webapp中的class和lib文件需要相互隔離,不能出現一個應用中加載的類庫會影響到另一個應用的情況。
- 安全性:與jvm相同,tomcat也期望使用單獨的classloader去裝載tomcat自身的類庫,以免其他惡意或者無意的破壞。
- 熱部署:tomcat修改文件可以不用重啟自動裝載類庫,這個點后面我們會單獨抽取示例。
思考5
- 既然ExtClassLoader是讀取特定目錄下的class文件,那么如果我將自定義的class文件移到
$JAVA_HOME/jre/lib/ext/目錄下是不是就能夠達到指定classloader加載類的目標呢? - 這個點是可以的,但是必須要求是jar。
思考6
- 類加載器常見的用途有類的隔離和熱替換,類的隔離非常好理解,那么熱替換呢?
- 仍然以我們之前的代碼為例,如下圖所示:

- 在程序運行過程中更新並編譯
Animal類中say方法 
- 可以看到運行結果如圖所示:

- ps:注意第一張圖中的紅色框框部分,如果修改為
Thread.currentThread().getContextClassLoader()會發現實際上不會起作用,這是由於要想實現同一個類的不同版本的共存,這些不同的版本必須由不同的類加載器進行加載,因此就不能把這些類的加載工作委托給類加載器來完成,因為它們只有一份。

