手把手教你写自定义类加载器

本文介绍了如何实现自定义的类加载器,并通过一个具体的示例演示了如何使其能够加载不在系统类路径中的类。

虚拟机类加载过程基础可以参考:JVM类加载机制基础

我们知道jvm的类加载是利用双亲委派机制,

图片来自http://blog.youkuaiyun.com/cutesource/article/details/5904501

JVM的类加载是通过ClassLoader及其子类来完成的,类的层次关系和加载顺序可以由下图来描述:

• 根类加载器(Bootstrap
• 扩展类加载器( Extension)
• 系统类加载器( App 




用户自定义类加载器需要继承ClassLoader类,ClassLoader的相关方法




jdk的ClassLoader类的loaderClass方法实现了双亲委派源码如下,lz加了部分注释


[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1.  protected Class<?> loadClass(String name, boolean resolve)  
  2.         throws ClassNotFoundException  
  3.     {  
  4.         synchronized (getClassLoadingLock(name)) {  
  5.             // First, check if the class has already been loaded  
  6.             Class<?> c = findLoadedClass(name);  
  7.             if (c == null) {  
  8.                 long t0 = System.nanoTime();  
  9.                 try {  
  10.                     if (parent != null) {     //如果此类加载器有父类加载器,那么先用父类类加载器加载  
  11.                         c = parent.loadClass(name, false);  
  12.                     } else {  
  13.                         c = findBootstrapClassOrNull(name);//如果父加载器为空,则默认用Bootstrap加载器加载  
  14.                     }  
  15.                 } catch (ClassNotFoundException e) {  
  16.                     // ClassNotFoundException thrown if class not found  
  17.                     // from the non-null parent class loader  
  18.                 }  
  19.   
  20.                 if (c == null) {  //如果所有的父加载器包括Bootstrap都不能加载此类,此时c为null,那么就调用用户自定义的类加载器的findClass方法进行加载,  
  21.                     // If still not found, then invoke findClass in order 所以用户自定义的类加载器要重写ClassLoader的findClass方法  
  22.                     // to find the class.  
  23.                     long t1 = System.nanoTime();  
  24.                     c = findClass(name);  
  25.   
  26.                     // this is the defining class loader; record the stats  
  27.                     sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);  
  28.                     sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);  
  29.                     sun.misc.PerfCounter.getFindClasses().increment();  
  30.                 }  
  31.             }  
  32.             if (resolve) {  
  33.                 resolveClass(c);  
  34.             }  
  35.             return c;  
  36.         }  
  37.     }</span>  


在写自己的类加载器之前,先看一下Java API中给出了一个网络类加载器:




下面我们参照这个例子,写一个自己的类加载器,因为网络不好模拟,我们就定义一个加载本地文件类的加载器。
首先创建一个Java工程,工程目录如下图所示




然后我们来写自己的类加载器MyClassLoader

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <span style="font-size:14px;">package com.slowly.hy;  
  2.   
  3. import java.io.ByteArrayOutputStream;  
  4. import java.io.FileInputStream;  
  5. import java.io.IOException;  
  6. import java.io.InputStream;  
  7.   
  8. public class MyClassLoader extends ClassLoader{  
  9.       
  10.     private String rootDir;//文件的根路径  
  11.       
  12.     public MyClassLoader(String rootDir){  
  13.         this.rootDir = rootDir;  
  14.     }  
  15.   
  16.     @Override  
  17.     protected Class<?> findClass(String name) throws ClassNotFoundException {  
  18.           
  19.         Class<?> c = findLoadedClass(name);//如果这个类已经加载了,直接返回  
  20.         if(c!=null){  
  21.             return c;  
  22.         }else{  
  23.             ClassLoader parent = this.getParent();  
  24.             c = parent.loadClass(name);  
  25.             if(c!=null){  
  26.                 return c;  
  27.             }else{  
  28.                 byte[] data = this.getClassData(name);  
  29.                 if(data==null){  
  30.                     throw new ClassNotFoundException();  
  31.                 }else{  
  32.                     c = defineClass(name, data, 0, data.length);  
  33.                 }  
  34.             }  
  35.               
  36.         }  
  37.         return c;  
  38.     }  
  39.     /** 
  40.      *  
  41.      * @param name 
  42.      * @return 返回所加载类class文件的字节数组 
  43.      */  
  44.     private byte[] getClassData(String name){  
  45.           
  46.          String path = rootDir+"/"+name.replace(".""/")+".class";  
  47.           
  48.         InputStream is = null;  
  49.         ByteArrayOutputStream baos = new ByteArrayOutputStream();  
  50.           
  51.         try {  
  52.             is = new FileInputStream(path);  
  53.             byte[] buffer = new byte[1024];  
  54.             int d = 0;  
  55.             while((d=is.read(buffer))!=-1){  
  56.                 baos.write(buffer, 0, d);  
  57.             }  
  58.               
  59.               
  60.         } catch (Exception e) {  
  61.             // TODO Auto-generated catch block  
  62.             e.printStackTrace();  
  63.         }finally{  
  64.             try {  
  65.                 is.close();  
  66.             } catch (IOException e) {  
  67.                 // TODO Auto-generated catch block  
  68.                 e.printStackTrace();  
  69.             }  
  70.         }  
  71.         return baos.toByteArray();  
  72.     }  
  73. }</span>  


定义一个被加载的类

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. package com.slowly.hy;  
  2.   
  3. public class Hello {  
  4.     public Hello(){  
  5.         System.out.println("Hello");  
  6.     }  
  7. }  


下面进行测试


package com.slowly.hy;

public class Test01 {
	
	public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException{
		MyClassLoader classLoader01 = new MyClassLoader("e:/project/MyClassLoaderLearning");//我的工程的路径
		Class<?> c = classLoader01.loadClass("com.slowly.hy.test.Hello");
		Object obj = c.newInstance();
	}
}

输出结果:
sun.misc.Launcher$AppClassLoader@73d16e93
Hello


可以看出此时使用系统类加载器加载的,并没有用我们自己的类加载器,这是什么原因呢?那么怎么又能让自己的类加载器进行加载呢??其实上面的双亲委派图中已经提到了,系统类加载器(App ClassLoader)会加载classpath或java.class.path路径下的jar包和类,下面我们让测试程序打印一下java.class.path


package com.slowly.hy;

public class Test02 {
	
	public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException{
		MyClassLoader classLoader01 = new MyClassLoader("e:/project/MyClassLoaderLearning");
		Class<?> c = classLoader01.loadClass("com.slowly.hy.test.Hello");
		System.out.println(c.getClassLoader());
		Object obj = c.newInstance();
		System.out.println(System.getProperty("java.class.path"));
	}
}




可以看出java.class.path路径为上图所示


那么怎样才能让自己定义的类加载器加载呢?我们把Hello.class文件移出系统类加载器的默认加载路径,

原路径:

移动到


测试程序

package com.slowly.hy;

public class Test03 {
	
	public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException{
		MyClassLoader classLoader01 = new MyClassLoader("e:/project/MyClassLoaderLearning");
		Class<?> c = classLoader01.loadClass("Hello");
		System.out.println(c.getClassLoader());
		Object obj = c.newInstance();
		System.out.println(System.getProperty("java.class.path"));
	}
}

运行结果


可以看出是用自己定义的类加载器加载了


写这篇文章真是太费劲了,发了3次,前面都是格式不对,发表上来比较乱,所以有重写写了一次,真是不容易啊,望好评,多指教,希望这次能发表成功吧!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值