ClassLoader
作用
ClassLoader是用來動態(tài)加載class文件到內(nèi)存中。
JAVA默認(rèn)提供三個classLoader
- BootStrap ClassLoader:啟動類加載器蹋绽,負(fù)責(zé)加載JDK中的核心類庫昧互。
- Extension ClassLoader:擴展類加載器,負(fù)責(zé)加載JAVA的擴展類庫薪介。
- App ClassLoader: 系統(tǒng)類加載器祠饺, 加載應(yīng)用程序classpath目錄下的所有jar和class文件。
- 自定義類加載器
ClassLoader類加載的原理
ClassLoader使用雙親委托來搜索類汁政,每一個ClassLoader實例都有一個父類加載器的引用道偷。
當(dāng)一個ClassLoader實例需要加載某個類時缀旁,會在親自搜索這個類之前,先把任務(wù)委托給父類加載器勺鸦,這個過程時由上至下的并巍。
首先由最頂層的類加載器BootStrap ClassLoader試圖加載,如果沒有加載到换途,則把任務(wù)轉(zhuǎn)交給Extension ClassLoader加載懊渡。
如果也沒加載到,則轉(zhuǎn)交給App ClassLoader進(jìn)行加載军拟。
如果也沒加載到剃执,則返回給委托的發(fā)起者,由它到指定的文件系統(tǒng)或者網(wǎng)絡(luò)等URL中加載該類懈息。
如果都沒有加載到這個類肾档,就拋出ClassNotFoundException異常。
否則將這個找到的類生成一個類的定義辫继,并將它加載到內(nèi)存中怒见,最后返回這個類在內(nèi)存中的Class實例對象。
使用原因
可以避免重復(fù)加載骇两,因為當(dāng)父類加載了該類的時候速种,子ClassLoader就沒有必要重新加載一次。
如果不使用這種委托模式低千,當(dāng)用戶可以使用自定義String來動態(tài)替換java核心api中定義的類型配阵。
而雙親委托的方式可以避免這種情況,因為String在啟動時已經(jīng)被BootStrap ClassLoader加載示血,用戶自定義的ClassLoader永遠(yuǎn)無法加載一個自己寫的String棋傍,除非改變JDK中ClassLoader搜索類的默認(rèn)算法。
JVM在搜索類的時候是如何判斷兩個Class是相同的难审?
不僅需要判斷兩個類的類名是否相同瘫拣,而且要判斷是否由同一個類加載器實例加載。只有同時滿足兩種情況告喊,JVM才認(rèn)為兩個Class是相同的麸拄。
就算兩個class是同一份class字節(jié)碼,如果被兩個不同的ClassLoader實例加載黔姜,JVM也會認(rèn)為是兩個不同的class拢切。
ClassLoader的體系架構(gòu)
定義自己的ClassLoader
JAVA提供了默認(rèn)的ClassLoader,為什么要定義自己的類加載器秆吵?
因為JAVA提供的默認(rèn)ClassLoader只加載指定目錄下的jar和Class淮椰,如果想加載其他位置的類或jar時,就需要定義自己的ClassLoader。目前的web服務(wù)器中都定義了自己的類加載器主穗,用于加載web應(yīng)用制定目錄下的類庫(jar或class)泻拦,入weblogic、Jboss忽媒、tomcat等争拐。
例子:
1、新建一個web工程httpweb
2晦雨、新建一個ClassLoaderServletTest陆错,用于打印web容器中的ClassLoader層次結(jié)構(gòu)
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ClassLoaderServletTest extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
ClassLoader loader = this.getClass().getClassLoader();
while(loader != null) {
out.write(loader.getClass().getName()+"<br/>");
loader = loader.getParent();
}
out.write(String.valueOf(loader));
out.flush();
out.close();
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doGet(request, response);
}
}
3、配置Servlet金赦,并啟動服務(wù)
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<servlet>
<servlet-name>ClassLoaderServletTest</servlet-name>
<servlet-class>ClassLoaderServletTest</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ClassLoaderServletTest</servlet-name>
<url-pattern>/servlet/ClassLoaderServletTest</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
4音瓷、訪問Servlet,獲得顯示結(jié)果
定義自己的類加載器分為兩步:
- 繼承java.lang.ClassLoader
- 重寫父類的findClass方法
當(dāng)JDK中的loadClass方法搜索不到類時夹抗,loadClass方法就會調(diào)用findClass方法搜索類绳慎,所以只需要重寫該方法即可。