本文共 7091 字,大约阅读时间需要 23 分钟。
类加载流程
Class1,Class2-> MyClassLoader1, MyClassLoader2, MyThreadClassLoader1,MyThreadClassLoader2 -> AppClassLoader -> ExtClassLoader -> BootStrap
每个类加载器方法流程
loadClass-> findClass -> defineClass(C++)
名称 | 描述 | 实现 |
Bootstrap | JVM内核,加载Jre/lib/rt.jar | C++,BootStrap |
Extension | 加载Jre/lib/ext/*.jar | Java,ExtClassLoader |
Application | 加载classpath指定位置,系统或应用类加载 | Java,AppClassLoader |
Custom | 自定义类的加载器,加载自定义位置class | Java |
Custom Thread | 自定义线程类加载器 | Java |
Launcher$ExtClassLoader和Launcher$AppClassLoader都是URLClassLoader的子类。
父类委托机制:一个类需要被加载的时候,JVM先会调用他的父类加载器进行加载(父类加载器,不一定他们之间是继承的关系,有可能仅仅是包装的关系)。如果父类加载器加载不了,再使用其子类进行加载。如果所有父类加载器都无法加载,再通过用户自定义的findClass方法进行加载。
JVM中类用全名和ClassLoader的实例作为唯一标识(包名,类名,ClassLoader实例名).
注:不要重写loadClass方法,如果重写了loadClass方法,就会导致jvm使用MyClassLoader来加载Object、String等等一些类。
<例子>Animal.java
package com.test;public class Animal { public void say(){ System.out.println("hello world 2~~~"); } }ClassLoaderTest02.java
package com.test;import java.io.BufferedInputStream;import java.io.ByteArrayOutputStream;import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.io.InputStream;import java.lang.reflect.Method;import java.net.URL;import java.net.URLClassLoader;public class ClassLoaderTest02 extends ClassLoader { private String path; // 加载类的路径 private final String fileType = ".class"; // .class文件扩展名 public ClassLoaderTest02(){ } public ClassLoaderTest02(ClassLoader parent){ super(parent); } public void setPath(String path) { this.path = path; } /** * 读取class文件作为二进制流放入到byte数组中去 * * @param name * @return */ private byte[] loadClassData(String name) { System.out.println("loadClassData~~~"); InputStream in = null; byte[] data = null; ByteArrayOutputStream baos = null; try { name = name.replace(".", "\\"); in = new BufferedInputStream(new FileInputStream(new File(path + name + fileType))); baos = new ByteArrayOutputStream(); int ch = 0; while (-1 != (ch = in.read())) { baos.write(ch); } data = baos.toByteArray(); } catch (Exception e) { e.printStackTrace(); } finally { try { in.close(); } catch (IOException e) { e.printStackTrace(); } finally { try { baos.close(); } catch (IOException e) { e.printStackTrace(); } } } return data; } /** * 定义class对应的命名空间 */ @Override protected Class findClass(String name) throws ClassNotFoundException { System.out.println("findClass~~~"); byte[] data = this.loadClassData(name); return this.defineClass(name, data, 0, data.length); } public static void main(String[] args) throws Exception { ClassLoaderTest02 loader1 = new ClassLoaderTest02(); ClassLoaderTest02 loader2 = new ClassLoaderTest02(loader1); // test01(loader1); // getParent~~~:com.test.AppClassLoader // test01(loader2); // getParent~~~:com.test.ClassLoaderTest02 // test02(loader1); // 自定義, loadClass > findClass > defineClass // test03(loader1); // 自定義, findClass > defineClass // test04(loader1); // 加载不同版本同名jar包 test05(loader1); // web环境 } /** * 正常使用AppClassLoader */ public static void test01(ClassLoaderTest02 loader) throws Exception { loader.setPath("D://workspace//java//javaTest" + "//bin//"); System.out.println("classLoader~~~:" + ClassLoaderTest02.class.getClassLoader().getClass().getName()); System.out.println("getParent~~~:" + loader.getParent().getClass().getName()); Class clazz = loader.loadClass("com.test.Animal"); // AppClassLoader System.out.println("clazz~~~:" + clazz.getClassLoader().getClass().getName()); // System.out.println("Animal~~~:" + Animal.class.getClassLoader()); // Animal animal=(Animal) clazz.newInstance(); // animal.say(); Object animalObj = clazz.newInstance(); Method animalSay = clazz.getMethod("say"); animalSay.invoke(animalObj); } /** * 使用自定義ClassLoader * 注:(1)需要先屏蔽classpath下的Animal.class(如重命名);(2)copy .class到其它目錄(如D:\temp1) */ public static void test02(ClassLoaderTest02 loader) throws Exception { loader.setPath("D://temp1//"); System.out.println("classLoader~~~:" + ClassLoaderTest02.class.getClassLoader().getClass().getName()); System.out.println("getParent~~~:" + loader.getParent().getClass().getName()); Class clazz = loader.loadClass("com.test.Animal"); // ClassLoaderTest02 System.out.println("clazz~~~:" + clazz.getClassLoader().getClass().getName()); Object animalObj = clazz.newInstance(); // classloader不一致, 通过reflect调用 Method animalSay = clazz.getMethod("say"); animalSay.invoke(animalObj); } /** * 使用自定義ClassLoader * 注:(1)不需要屏蔽classpath下的Animal.class;(2)copy .class到其它目錄(如D:\temp1) */ public static void test03(ClassLoaderTest02 loader) throws Exception { loader.setPath("D://temp1//"); System.out.println("classLoader~~~:" + ClassLoaderTest02.class.getClassLoader().getClass().getName()); System.out.println("getParent~~~:" + loader.getParent().getClass().getName()); Class clazz = loader.findClass("com.test.Animal"); // 直接调用 System.out.println("clazz~~~:" + clazz.getClassLoader().getClass().getName()); Object animalObj = clazz.newInstance(); // classloader不一致, 通过reflect调用 Method animalSay = clazz.getMethod("say"); animalSay.invoke(animalObj); } /** * 使用自定義ClassLoader,加载不同版本同名jar包 * 注:(1)需要先屏蔽classpath下的Animal.class(如重命名);(2)copy .class到其它目錄(如\jar) */ public static void test04(ClassLoaderTest02 loader) throws Exception { URL url1 = new URL("file:D://workspace//jar//Animal-0.0.1.jar"); // 输出为"hello world 1!" URL url2 = new URL("file:D://workspace//jar//Animal-0.0.2.jar"); // 输出为"hello world 2~~~" URLClassLoader myClassLoader1 = new URLClassLoader( new URL[] { url1 } ); URLClassLoader myClassLoader2 = new URLClassLoader( new URL[] { url2 } ); System.out.println("myClassLoader1~~~" + myClassLoader1.getClass()); Class clazz1 = myClassLoader1.loadClass("com.test.Animal"); Class clazz2 = myClassLoader2.loadClass("com.test.Animal"); Object animalObj1 = clazz1.newInstance(); Object animalObj2 = clazz2.newInstance(); Method animalSay1 = clazz1.getMethod("say"); Method animalSay2 = clazz2.getMethod("say"); animalSay1.invoke(animalObj1); animalSay2.invoke(animalObj2); } /** * web环境 * JVM中类用全名和ClassLoader的实例作为唯一标识 * 参考:CSDN -> "Peter_K的专栏" -> "ClassLoader解决jar包冲突问题" */ public static void test05(ClassLoaderTest02 loader) throws Exception { URL url1 = new URL("file:D://workspace//jar//Animal-0.0.1.jar"); System.out.println("ContextClassLoader~~~" + Thread.currentThread().getContextClassLoader()); System.out.println("ContextClassLoader-getParent~~~" + Thread.currentThread().getContextClassLoader().getParent()); URLClassLoader myClassLoader1 = new URLClassLoader( new URL[] { url1 } , Thread.currentThread().getContextClassLoader()); // 当前线程类加载器 System.out.println("myClassLoader1~~~" + myClassLoader1.getClass()); Class clazz1 = myClassLoader1.loadClass("com.test.Animal"); Object animalObj1 = clazz1.newInstance(); Method animalSay1 = clazz1.getMethod("say"); animalSay1.invoke(animalObj1); } /* * 更多参考: * 1)ITEye -> "classloader 加载同名类问题" * 2)http://blog.csdn.net/is_zhoufeng/article/details/26602689 */}文件位置