Java ClassLoader 原理详细分析
作者:网络转载 发布时间:[ 2015/3/24 11:11:46 ] 推荐标签:Java ClassLoader JDK
读者可能在这里有疑问,父类有那么多方法,为什么偏偏只重写findClass方法?
因为JDK已经在loadClass方法中帮我们实现了ClassLoader搜索类的算法,当在loadClass方法中搜索不到类时,loadClass方法会调用findClass方法来搜索类,所以我们只需重写该方法即可。如没有特殊的要求,一般不建议重写loadClass搜索类的算法。下图是API中ClassLoader的loadClass方法:

示例:自定义一个NetworkClassLoader,用于加载网络上的class文件
package classloader;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.URL;
/**
*加载网络class的ClassLoader
*/
public class NetworkClassLoader extends ClassLoader{
private String rootUrl;
public NetworkClassLoader(String rootUrl){
this.rootUrl=rootUrl;
}
@Override
protected Class<?>findClass(String name)throws ClassNotFoundException{
Class clazz=null;//this.findLoadedClass(name);//父类已加载
//if(clazz==null){//检查该类是否已被加载过
byte[]classData=getClassData(name);//根据类的二进制名称,获得该class文件的字节码数组
if(classData==null){
throw new ClassNotFoundException();
}
clazz=defineClass(name,classData,0,classData.length);//将class的字节码数组转换成Class类的实例
//}
return clazz;
}
private byte[]getClassData(String name){
InputStream is=null;
try{
String path=classNameToPath(name);
URL url=new URL(path);
byte[]buff=new byte[1024*4];
int len=-1;
is=url.openStream();
ByteArrayOutputStream baos=new ByteArrayOutputStream();
while((len=is.read(buff))!=-1){
baos.write(buff,0,len);
}
return baos.toByteArray();
}catch(Exception e){
e.printStackTrace();
}finally{
if(is!=null){
try{
is.close();
}catch(IOException e){
e.printStackTrace();
}
}
}
return null;
}
private String classNameToPath(String name){
return rootUrl+"/"+name.replace(".","/")+".class";
}
}
测试类:
package classloader;
public class ClassLoaderTest{
public static void main(String[]args){
try{
/*ClassLoader loader=ClassLoaderTest.class.getClassLoader();//获得ClassLoaderTest这个类的类加载器
while(loader!=null){
System.out.println(loader);
loader=loader.getParent();//获得父加载器的引用
}
System.out.println(loader);*/
String rootUrl="http://localhost:8080/httpweb/classes";
NetworkClassLoader networkClassLoader=new NetworkClassLoader(rootUrl);
String classname="org.classloader.simple.NetClassLoaderTest";
Class clazz=networkClassLoader.loadClass(classname);
System.out.println(clazz.getClassLoader());
}catch(Exception e){
e.printStackTrace();
}
}
}
打印结果:

下图是我机器上web服务器的目录结构:

目前常用web服务器中都定义了自己的类加载器,用于加载web应用指定目录下的类库(jar或class),如:Weblogic、Jboss、tomcat等,下面我以Tomcat为例,展示该web容器都定义了哪些个类加载器:
1、新建一个web工程httpweb
2、新建一个ClassLoaderServletTest,用于打印web容器中的ClassLoader层次结构
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,并启动服务
<?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,获得显示结果


sales@spasvo.com