java類加載相關

類加載機制你們應該已經很是熟悉了,採起雙親委派機制,當加載一個類時,首先將加載任務委託給父類加載器,依次遞歸,若是父類加載器能夠完成加載任務,就成功返回;若是父類沒法加載,才由本身加載。 java

雙親委派機制的做用:防止內存中出現多份相同的字節碼。mysql

其餘規則
1.隱式加載:在當前類中全部new的對象,若是沒有被加載,則使用當前類的類加載器加載 若是類A中引用了類B,Java虛擬機將使用加載類A的類加載器去加載類B
2.不一樣類加載器加載的類是不一樣的,經過類加載器+類全路徑來惟一標識一個類web

JVM預約義的三種類加載器
1.Bootstrap ClassLoader:啓動類加載器,它負責將JAVA_HOME/lib下面的類庫加載到內存中,如rt.jar;啓動類加載器是由C++寫的二進制代碼,不是java類,在JVM啓動的時候Bootstrap就已經啓動。
2.Extension ClassLoader:標準擴展類加載器,它負責加載JAVA_HOME/lib/ext或由系統變量java.ext.dir指定位置中的類庫加載到內存中。
3.APP ClassLoader:系統類加載器(System ClassLoader),它負責將類路徑CLASSPATH中的類庫加載到內存。sql

加載順序圖以下:數據庫

圖片描述

圖中的BootStrapClassLoader、ExtClassLoader、APPClassLoader不是真正的繼承關係,只是邏輯上的上下級類加載器;tomcat

實際上的類關係以下圖:服務器

圖片描述

能夠看到ExtClassLoader和APPCLassLoader都繼承自URLClassLoader,也就證明了兩者並不是真正的繼承關係;oracle

經過上圖能夠看到最頂層的類是抽象類ClassLoader:是全部類加載器的基類(除了啓動類加載器),定義了類加載最核心的操做;
SecureClassLoader:添加了關聯類源碼、關聯繫統權限支持
URLClassLoader:支持從jar文件和文件夾中獲取class
ExtClassLoader:擴展類加載器Extension ClassLoader
APPClassLoader:系統類加載器,也稱爲System ClassLoaderapp

ClassLoader:webapp

父子類加載器是經過ClassLoader一個parent屬性來標識,APPClassLoader的父加載器是ExtClassLoader,ExtClassLoader的父加載器是null。

ClassLoader提供了兩個構造器,一個是沒有參數的,一個是有參數的;以下圖:

圖片描述

圖片描述

沒有參數的構造器默認將系統類加載器做爲parent加載器;
有參數的構造器將參數指定的加載器做爲父類加載器;

Launcher:

ExtClassLoader和AppClassLoader都是Launcher的子類,在ClassLoader初始化或者直接經過ClassLoader的getSystemClassLoader()獲取的時候會調用initSystemClassLoader(),從而調用sun.misc.Launcher.getLauncher(),將系統類加載器賦值給ClassLoader的scl變量;

咱們看下Launcher類初始化的時候都作了什麼工做,如圖:

圖片描述

主要是三部工做:
1.建立ExtClassLoader
2.建立AppClassLoader
3.將線程系統類加載器設置爲線程上下文類加載器,什麼是上線文類加載器?

線程上下文類加載器
java提供了爲不少服務商提供了接口,簡稱SPI(Service Provider Interface),具體的實現由各廠商提供,例如mysql驅動,oracle驅動等。例如:mysql驅動加載接口類在rt.jar中,由啓動類加載器加載,具體實現類在mysql驅動包中,驅動包通常放到咱們本身的程序路徑lib下,應該由系統類加載器加載;可是在使用以下代碼進行數據庫鏈接使用操做的時候,就會出如今rt.jar中要加載驅動包裏代碼的狀況(類加載器是啓動類加載器),由隱式加載規則可知,驅動包也要使用啓動類加載器加載,由類加載機制可知,是沒法經過啓動類加載器來加載的;那這種狀況怎麼辦呢,就要經過線程上下文類加載器來解決。

上面描述的狀況以下:使用jdbc進行數據庫操做以下

1.Class.forName("com.mysql.jdbc.Driver");// 加載mysql驅動
2.Connection conn = DriverManager.getConnection(url);//建立鏈接
3.Statement stmt = conn.createStatement();//獲得statement對象
4.操做數據庫  關閉鏈接。。

第一步在實例化Driver時,會調用DriverManager的registerDriver()方法收集divers,將驅動類註冊到DriverManager容器中,DriverManage的drivers容器:

圖片描述

註冊的代碼:

圖片描述

Class.forName("com.mysql.jdbc.Driver")至關於:
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class driversClass = loader.loadClass("com.mysql.jdbc.Driver");
driversClass.newInstance();
因而可知com.mysql.jdbc.Driver是由咱們本身應用類加載器AppClassLoader進行加載;

第二步經過DriverManager.getConnection(url),會循環獲取drivers中的driver,調用具體driver實現裏的cnnect()方法,進行鏈接

圖片描述

caller.getClassLoader()是啓動類加載器爲null,所以callerCL爲系統類加載器

圖片描述

getConnection經過isDriverAllowed方法校驗類是否有權限被加載

圖片描述

經過AppClassLoader來加載Driver看是否和已註冊的Driver是同一個類,若是是則調用driver的connect方法

在java6之後,引入了service provider概念,在/META-INF/services/java.sql.Driver文件中配置須要加載的驅動類,
在DriverManager初始化的時候會調用loadInitialDrivers方法,

圖片描述

會使用AppClassLoader進行加載,因此在本身程序中能夠不用Class.forName顯示調用。

上面包類結構以下圖:

圖片描述

tomcat類加載
咱們運行tomcat的多個實例,不想安裝tomcat軟件副本,咱們能夠配置多個工做目錄,每一個運行實例獨佔一個工做目錄,可是共享一個安裝目錄。
變量解釋:
CATALINA_HOME:tomcat安裝目錄,多個工做目錄可共享安裝目錄
CATALINA_BASE:tomcat工做目錄,tomcat每一個運行實例須要使用本身的conf、logs、temp、webapps、work、shared目錄,CATALINA_BASE就是指向這個目錄

以下圖:兩個應用公用CATALINA_HOME,CATALINA_BASE指向各自工做目錄

圖片描述
圖片描述

首先看下tomcat在啓動時類初始化類加載器過程

圖片描述

首先是建立commonClassLoader,commonClassLoader加載的是配置文件catalina.properties中配置的

圖片描述

common.loader=${catalina.home}/lib,${catalina.home}/lib/*.jar
server.loader=
shared.loader=
例如在咱們的服務器上路徑是:
/opt/soft/tomcat/lib、/opt/soft/tomcat/lib/*.jar

由於server.loader和shared.loader未配置具體加載目錄信息,catalinaLoader和sharedLoader默認爲commonLoader(在tomcat5之後catalinaLoader和sharedLoader默認不啓用)

圖片描述

tomcat一共定義了兩種類加載器
StandardClassLoader:實例化commonloader、catalinaLoader、sharedLoader,不提供熱部署功能,遵循雙親委派機制
WebappClassLoader:和context級容器相關聯,加載web程序,支持其加載路徑下資源改變後從新加載,不遵循雙親委派機制。

其類繼承關係以下:

圖片描述

除此以外還有兩個類:WebappLoader和VirtualWebappLoader,該兩個類不是類加載器,只是對WebappClassLoader作了封裝,對熱部署、生命週期控制等功能作了一些控制;
VirtualWebappLoader是WebappLoader的子類,主要是和conf/context.xml這個文件相關聯,主要功能是加載context.xml配置文件中設置的一些java類庫,因爲WebappClassLoader只能加載WEB-INF/class和WEB-INF/lib下的類庫。而想擴展一下加載路徑又不想添加到WEB-INF/lib中的時候,能夠配置在context.xml文件中。

本身實現類記載器只要實現findclass便可,這裏爲了實現特殊目的而override了loadClass();WebappClassLoader重寫了loadClass方法,先本身加載,若是加載不了再進行其餘操做。

因此在Tomcat 6中默認狀況下,不是徹底按照先Tomcat的lib再Web應用的lib這種順序去加載類。
Jar包的加載順序是:
1)JRE中的Java基礎包
2)Web應用WEB-INF/lib下的包
3)Tomcat/lib下的包

若是想要在Web應用間共享一些Jar包,則不只須要將公共包放在Tomcat的lib下,還要刪掉Web應用lib中的包,不然Tomcat啓動時仍是會優先加載Web應用lib下的包的。

若是想要本身指定一個Tomcatlib和Web應用lib以外的ClassPath,除了修改Tomcat啓動腳本外,能夠爲不一樣Web應用的Context指定一個VirtualWebappLoader,但源碼註釋中寫到不推薦在生產環境中使用。

相關文章
相關標籤/搜索