详细的原理就不多说了,网上一大把, 但是, 看了很多很多, 即使看了jdk 源码, 说了罗里吧嗦, 还是不很明白:
到底如何正确自定义ClassLoader, 需要注意什么
ExtClassLoader 是什么鬼
自定义ClassLoader具体是如何加载 类的。。
直接上代码:
import java.io.ByteArrayOutputStream;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;import java.io.InputStream;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.nio.ByteBuffer;import java.nio.channels.Channels;import java.nio.channels.FileChannel;import java.nio.channels.WritableByteChannel;import java.util.Date;import com.lk.AbcBean;public class ClassLoaderLK extends ClassLoader { /** * @param args */ public static void main(String[] args) {// this.class.getSystemClassLoader(); String ext = "java.ext.dirs"; System.out.println("java.ext.dirs :\n" + System.getProperty(ext)); String cp = "java.class.path"; System.out.println("java.class.path :\n" + System.getProperty(cp)); ClassLoader currentClassloader = ClassLoaderLK.class.getClassLoader(); String pp = "d:\\testcl\\"; ClassLoaderLK cl = new ClassLoaderLK(currentClassloader, pp); System.out.println(); System.out.println("currentClassloader is " + currentClassloader); System.out.println(); String name = "com.lk.AbcBean.class"; name = "com.lk.AbcBean"; try { Class loadClass = cl.loadClass(name); Object object = loadClass.newInstance(); // AbcBean ss = (AbcBean) object; // 无法转换的 (1)// ss.greeting(); (1) System.out.println(); System.out.println(" invoke some method !"); System.out.println(); Method method = loadClass.getMethod("greeting"); method.invoke(object); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NoSuchMethodException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private ClassLoader parent = null; // parent classloader private String path; public ClassLoaderLK(ClassLoader parent, String path) { super(parent); this.parent = parent; // 这样做其实是无用的 this.path = path; } public ClassLoaderLK(String path) { this.path = path; } @Override public Class loadClass(String name) throws ClassNotFoundException {// return super.loadClass(name); Class cls = findLoadedClass(name); if (cls == null) {// cls = getSystemClassLoader().loadClass(name); (2)// SystemClassLoader 会从classpath下加载// if (cls == null) {(2) // 默认情况下, 当前cl的parent是 SystemClassLoader, // 而当前cl的parent的parent 才是ExtClassLoader ClassLoader parent2 = getParent().getParent();// System.out.println("Classloader is : " + parent2); try { System.out.println("try to use ExtClassLoader to load class : " + name); cls = parent2.loadClass(name); } catch (ClassNotFoundException e) { System.out.println("ExtClassLoader.loadClass :" + name + " Failed"); }// }(2) if (cls == null) { System.out.println("try to ClassLoaderLK load class : " + name); cls = findClass(name); if (cls == null) { System.out.println("ClassLoaderLK.loadClass :" + name + " Failed"); } else { System.out.println("ClassLoaderLK.loadClass :" + name + " Successful"); } } else { System.out.println("ExtClassLoader.loadClass :" + name + " Successful"); } } return cls; } @Override @SuppressWarnings("rawtypes") protected Class findClass(String name) throws ClassNotFoundException {// return super.findClass(name); System.out.println( "try findClass " + name); InputStream is = null; Class class1 = null; try { String classPath = name.replace(".", "\\") + ".class";// String[] fqnArr = name.split("\\."); // split("."); 是不行的, 必须split("\\.")// if (fqnArr == null || fqnArr.length == 0) {// System.out.println("ClassLoaderLK.findClass()");// fqnArr = name.split("\\.");// } else {// System.out.println( name + fqnArr.length);// } String classFile = path + classPath; byte[] data = getClassFileBytes(classFile ); class1 = defineClass(name, data, 0, data.length); if (class1 == null) { System.out.println("ClassLoaderLK.findClass() ERR "); throw new ClassFormatError(); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } finally { if (is != null) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } return class1; } private byte[] getClassFileBytes(String classFile) throws Exception { FileInputStream fis = new FileInputStream(classFile ); FileChannel fileC = fis.getChannel(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); WritableByteChannel outC = Channels.newChannel(baos); ByteBuffer buffer = ByteBuffer.allocateDirect(1024); while (true) { int i = fileC.read(buffer); if (i == 0 || i == -1) { break; } buffer.flip(); outC.write(buffer); buffer.clear(); } fis.close(); return baos.toByteArray(); } }
随便的一个java 类, 简单起见,就写一个bean吧
package com.lk;import java.util.Date;public class AbcBean { @Override public String toString() { return "AbcBean [name=" + name + ", age=" + age + "]"; } String name; int age; Date birthDay; public Date getBirthDay() { return birthDay; } public void setBirthDay(Date birthDay) { this.birthDay = birthDay; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public void greeting() { System.out.println("AbcBean.greeting()"); }}
直接执行,结果:
currentClassloader is sun.misc.Launcher$AppClassLoader@513cf0try to use ExtClassLoader to load class : com.lk.AbcBeanExtClassLoader.loadClass :com.lk.AbcBean Failedtry to ClassLoaderLK load class : com.lk.AbcBeantry findClass com.lk.AbcBeanjava.io.FileNotFoundException: d:\testcl\com\lk\AbcBean.class (系统找不到指定的路径。) at java.io.FileInputStream.open(Native Method) at java.io.FileInputStream.(FileInputStream.java:138) at java.io.FileInputStream. (FileInputStream.java:97) at ClassLoaderLK.getClassFileBytes(ClassLoaderLK.java:172) at ClassLoaderLK.findClass(ClassLoaderLK.java:145) at ClassLoaderLK.loadClass(ClassLoaderLK.java:112) at ClassLoaderLK.main(ClassLoaderLK.java:38)Exception in thread "main" java.lang.NullPointerException at ClassLoaderLK.main(ClassLoaderLK.java:40)ClassLoaderLK.loadClass :com.lk.AbcBean Failed
将com.lk 目录全部复制到 d:\\testcl\\ 下,
currentClassloader is sun.misc.Launcher$AppClassLoader@513cf0try to use ExtClassLoader to load class : com.lk.AbcBean ExtClassLoader.loadClass :com.lk.AbcBean Failed try to ClassLoaderLK load class : com.lk.AbcBean try findClass com.lk.AbcBean try to use ExtClassLoader to load class : java.lang.Object ExtClassLoader.loadClass :java.lang.Object Successful ClassLoaderLK.loadClass :com.lk.AbcBean Successful invoke some method ! try to use ExtClassLoader to load class : java.lang.String ExtClassLoader.loadClass :java.lang.String Successful try to use ExtClassLoader to load class : java.lang.System ExtClassLoader.loadClass :java.lang.System Successful try to use ExtClassLoader to load class : java.io.PrintStream ExtClassLoader.loadClass :java.io.PrintStream Successful AbcBean.greeting()
将AbcBean打包成 jar 放置到 jdk 下的jre 的ext目录 ( 打包成 zip 也是可行的! 但是rar是不行的!!! why ? 估计zip和jar都是使用的java 的zip流, 而rar是后面产生的新格式,故没有被支持。另外, 仅仅拷贝class 过去也是不行的! )
执行结果:
currentClassloader is sun.misc.Launcher$AppClassLoader@513cf0try to use ExtClassLoader to load class : com.lk.AbcBeanExtClassLoader.loadClass :com.lk.AbcBean Successful invoke some method !AbcBean.greeting()
可见ExtClassLoader 是如何作用了的吧!!
总结,
1 从 invoke some method 前后的日志,可见 类加载 的大致过程。
2 代码中 (1), 的部分是注释了的, 因为 不同类加载器加载的类是 不能直接cast的。。 但把(1),(2) 同时解开注释, 又可以了, 这是因为他们都是使用的系统类加载器, 自定义的类加载器相当于没有生效。。( 这个当然不是我们需要的结果。)
3 loadClass, findClass 两个方法的复写是必须的。 上面代码中的loadClass 的写法其实有点问题, 参照classloader 源码, 应该还需要一步: parent加载不上了, 使用bootstrap 加载, 不过感觉一般应该是用不上的—— 谁需要去替换 jdk 的rt.jar 的类 ??
4 ExtClassLoader 是去加载 jdk 下 jre ext 目录的类似jar 的文件—— 后缀是不是jar 不要紧, 内容是jar就行了。