不同的classloader加载的相同的类,会被jvm认为是不同的类
要想实现热加载,几个原则是要记住的:
每次实例化新的classloader
动态加载类文件,比如rul或者文件等等
记载的类使用反射进行方法调用,或者上溯为接口进行调用。
下面看一个例子:
首先定义一个被调用的简单类AppObject:
package com.dataguru.jvm.classloader; public class AppObject { public void sayHello(){ System.out.println("Hello 1."); } }
然后自定义一个ClassLoader的子类HotClassLoader:
package com.dataguru.jvm.classloader; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.Class; import java.lang.ClassLoader; import java.lang.ClassNotFoundException; import java.lang.Override; import java.lang.String; import java.lang.System; public class HotClassLoader extends ClassLoader { public HotClassLoader() { // TODO Auto-generated constructor stub } public HotClassLoader(ClassLoader cl) { super(cl); } @Override public Class<?> loadClass(String name,boolean resolve) throws ClassNotFoundException { { // First, check if the class has already been loaded Class<?> re = null; try{ re = findClass(name); }catch(SecurityException se){ System.out.println(se.getMessage()); } if(re == null){ System.out.println("无法载入类:"+name+" 需要请求父加载器"); return super.loadClass(name,resolve); } return re; } } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { Class<?> cl = null; try { byte[] data = loadClassBytes(name); cl = super.defineClass(name, data, 0, data.length); } catch (IOException e) { e.printStackTrace(); } return cl; } public static byte[] loadClassBytes(String clazzName) throws IOException{ String resourceName = "/".concat(clazzName.replaceAll("[.]", "/")).concat(".class"); System.out.println("resource Location:"+resourceName); InputStream is = TestMain.class.getResourceAsStream(resourceName); ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] bt = new byte[1024]; int l = 0; while((l = is.read(bt)) > 0 ){ baos.write(bt,0,l); } byte[] rst= baos.toByteArray(); is.close(); baos.close(); return rst; } }
最后写测试类进行测试:
/** * */ package com.dataguru.jvm.classloader; import java.lang.reflect.Method; /** * @author ShenLi * */ public class TestHotLoad { /** * @param args * @throws Exception */ public static void main(String[] args) throws Exception { HotClassLoader mcl = null; while(true){ mcl = new HotClassLoader(); Class<?> clz = mcl.loadClass("com.dataguru.jvm.classloader.AppObject", true); mcl = new HotClassLoader(); Object o = clz.newInstance(); System.out.println(o); Method m = o.getClass().getMethod("sayHello", new Class[] {}); m.invoke(o, new Object[] {}); Thread.sleep(5000); } } }
注意,每次生成新的classLoader实例,是因为通一个classLoader不能两次加载相同的类,否则会报错。生成的类实例,也不能直接new AppObject()而是要通过反射来调用,或者上溯为接口(还未测试)
测试输出:
resource Location:/com/dataguru/jvm/classloader/AppObject.class resource Location:/java/lang/Object.class Prohibited package name: java.lang 无法载入类:java.lang.Object 需要请求父加载器 com.dataguru.jvm.classloader.AppObject@33909752 resource Location:/java/lang/System.class Prohibited package name: java.lang 无法载入类:java.lang.System 需要请求父加载器 resource Location:/java/io/PrintStream.class Prohibited package name: java.io 无法载入类:java.io.PrintStream 需要请求父加载器 Hello 1.
将程序改为hello 2后保存编译
这时测试程序并没有退出,而是继续输出,但是输出改变了:
resource Location:/com/dataguru/jvm/classloader/AppObject.class resource Location:/java/lang/Object.class Prohibited package name: java.lang 无法载入类:java.lang.Object 需要请求父加载器 com.dataguru.jvm.classloader.AppObject@7f31245a resource Location:/java/lang/System.class Prohibited package name: java.lang 无法载入类:java.lang.System 需要请求父加载器 resource Location:/java/io/PrintStream.class Prohibited package name: java.io 无法载入类:java.io.PrintStream 需要请求父加载器 Hello 2.
之后反复修改类,均可以实现动态加载。
原文地址:http://power9li.blog.51cto.com/8267716/1616062