雙親委派
當一個類加載器去加載類時先嚐試讓父類加載器去加載,如果父類加載器加載不了再嘗試自身加載。這也是我們在自定義ClassLoader時java官方建議遵守的約定。
雙親委派模型能保證基礎類僅加載一次,不會讓jvm中存在重名的類。比如String.class,每次加載都委託給父加載器,最終都是BootstrapClassLoader,都保證java核心類都是BootstrapClassLoader加載的,保證了java的安全與穩定性。
自己實現ClassLoader時只需要繼承ClassLoader類,然後覆蓋findClass(String name)方法即可完成一個帶有雙親委派模型的類加載器。
破壞雙親委派模型
1代碼熱替換,在不重啓服務器的情況下可以修改類的代碼並使之生效。
熱部署步驟:
-
銷燬自定義classloader(被該加載器加載的class也會自動卸載);
-
更新class
-
使用新的ClassLoader去加載class
JVM中的Class只有滿足以下三個條件,才能被GC回收,也就是該Class被卸載(unload):
-
該類所有的實例都已經被GC,也就是JVM中不存在該Class的任何實例。
-
加載該類的ClassLoader已經被GC。
-
該類的java.lang.Class 對象沒有在任何地方被引用,如不能在任何地方通過反射訪問該類的方法
2JDBC
-
第一,從META-INF/services/java.sql.Driver文件中獲取具體的實現類名“com.mysql.jdbc.Driver”
-
第二,加載這個類,這裏肯定只能用class.forName("com.mysql.jdbc.Driver")來加載
好了,問題來了,Class.forName()加載用的是調用者的Classloader,這個調用者DriverManager是在rt.jar中的,ClassLoader是啓動類加載器,而com.mysql.jdbc.Driver肯定不在/lib下,所以肯定是無法加載mysql中的這個類的。這就是雙親委派模型的侷限性了,父級加載器無法加載子級類加載器路徑中的類。
那麼,這個問題如何解決呢?按照目前情況來分析,這個mysql的drvier只有應用類加載器能加載,那麼我們只要在啓動類加載器中有方法獲取應用程序類加載器,然後通過它去加載就可以了。這就是所謂的線程上下文加載器。
線程上下文類加載器讓父級類加載器能通過調用子級類加載器來加載類,這打破了雙親委派模型的原則
簡單的說就是破壞了可見性
3tomcat中的每個項目之間能加載不用的lib