原文:http://www.blogjava.net/heavensay/archive/2012/11/07/389685.html
这篇文章主要是分析Tomcat中关于热部署和jsp更新替换的原理,在此之前先介绍class的热替换和class的卸载的原理。
一 class的热替换ClassLoader中重要的方法loadClass ClassLoader.loadClass(...) 是ClassLoader的入口点。当一个类没有指明用什么加载器加载的时候,JVM默认采用AppClassLoader加载器加载没有加载过的class,调用的方法的入口就是loadClass(...)。如果一个class被自定义的ClassLoader加载,那么JVM也会调用这个自定义的ClassLoader.loadClass(...)方法来加载class内部引用的一些别的class文件。重载这个方法,能实现自定义加载class的方式,抛弃双亲委托机制,但是即使不采用双亲委托机制,比如java.lang包中的相关类还是不能自定义一个同名的类来代替,主要因为JVM解析、验证class的时候,会进行相关判断。 defineClass 系统自带的ClassLoader,默认加载程序的是AppClassLoader,ClassLoader加载一个class,最终调用的是defineClass(...)方法,这时候就在想是否可以重复调用defineClass(...)方法加载同一个类(或者修改过),最后发现调用多次的话会有相关错误:...java.lang.LinkageError attempted duplicate class definition...所以一个class被一个ClassLoader实例加载过的话,就不能再被这个ClassLoader实例再次加载(这里的加载指的是,调用了defileClass(...)放方法,重新加载字节码、解析、验证。)。而系统默认的AppClassLoader加载器,他们内部会缓存加载过的class,重新加载的话,就直接取缓存。所与对于热加载的话,只能重新创建一个ClassLoader,然后再去加载已经被加载过的class文件。下面看一个class热加载的例子:代码:HotSwapURLClassLoader自定义classloader,实现热替换的关键 1 package testjvm.testclassloader; 2 3 import java.io.File; 4 import java.io.FileNotFoundException; 5 import java.net.MalformedURLException; 6 import java.net.URL; 7 import java.net.URLClassLoader; 8 import java.util.HashMap; 9 import java.util.Map; 10 11 /** 12 * 只要功能是重新加载更改过的.class文件,达到热替换的作用 13 * @author banana 14 */ 15 public class HotSwapURLClassLoader extends URLClassLoader { 16 //缓存加载class文件的最后最新修改时间 17 public static Map<String,Long> cacheLastModifyTimeMap = new HashMap<String,Long>(); 18 //工程class类所在的路径 19 public static String PRojectClassPath = "D:/Ecpworkspace/ZJob-Note/bin/"; 20 //所有的测试的类都在同一个包下 21 public static String packagePath = "testjvm/testclassloader/"; 22 23 private static HotSwapURLClassLoader hcl = new HotSwapURLClassLoader(); 24 25 public HotSwapURLClassLoader() { 26 //设置ClassLoader加载的路径 27 super(getMyURLs()); 28 } 29 30 public static HotSwapURLClassLoader getClassLoader(){ 31 return hcl; 32 } 33 34 private static URL[] getMyURLs(){ 35 URL url = null; 36 try { 37 url = new File(projectClassPath).toURI().toURL(); 38 } catch (MalformedURLException e) { 39 e.printStackTrace(); 40 } 41 return new URL[] { url }; 42 } 43 44 /** 45 * 重写loadClass,不采用双亲委托机制("java."开头的类还是会由系统默认ClassLoader加载) 46 */ 47 @Override 48 public Class<?> loadClass(String name,boolean resolve) throws ClassNotFoundException { 49 Class clazz = null; 50 //查看HotSwapURLClassLoader实例缓存下,是否已经加载过class 51 //不同的HotSwapURLClassLoader实例是不共享缓存的 52 clazz = findLoadedClass(name); 53 if (clazz != null ) { 54 if (resolve){ 55 resolveClass(clazz); 56 } 57 //如果class类被修改过,则重新加载 58 if (isModify(name)) { 59 hcl = new HotSwapURLClassLoader(); 60 clazz = customLoad(name, hcl); 61 } 62 return (clazz); 63 } 64 65 //如果类的包名为"java."开始,则有系统默认加载器AppClassLoader加载 66 if(name.startsWith("java.")){ 67 try { 68 //得到系统默认的加载cl,即AppClassLoader 69 ClassLoader system = ClassLoader.getSystemClassLoader(); 70 clazz = system.loadClass(name); 71 if (clazz != null) { 72 if (resolve) 73 resolveClass(clazz); 74 return (clazz); 75 } 76 } catch (ClassNotFoundException e) { 77 // Ignore 78 } 79 } 80 81 return customLoad(name,this); 82 } 83 84 public Class load(String name) throws Exception{ 85 return loadClass(name); 86 } 87 88 /** 89 * 自定义加载 90 * @param name 91 * @param cl 92 * @return 93 * @throws ClassNotFoundException 94 */ 95 public Class customLoad(String name,ClassLoader cl) throws ClassNotFoundException { 96 return customLoad(name, false,cl); 97 } 98 99 /**100 * 自定义加载101 * @param name102 * @param resolve103 * @return104 * @throws ClassNotFoundException105 */106 public Class customLoad(String name, boolean resolve,ClassLoader cl)107 throws ClassNotFoundException {108 //findClass(- 该类所有的实例都已经被GC。 - 加载该类的ClassLoader实例已经被GC。 - 该类的java.lang.Class对象没有在任何地方被引用。
GC的时机我们是不可控的,那么同样的我们对于Class的卸载也是不可控的。 例子:代码:SimpleURLClassLoader,一个简单的自定义classloader 1 package testjvm.testclassloader; 2 3 import java.io.File; 4 import java.net.MalformedURLException; 5 import java.net.URL; 6 import java.net.URLClassLoader; 7 8 public class SimpleURLClassLoader extends URLClassLoader { 9 //工程class类所在的路径 10 public static String projectClassPath = "E:/IDE/work_place/ZJob-Note/bin/"; 11 //所有的测试的类都在同一个包下 12 public static String packagePath = "testjvm/testclassloader/"; 13 14 public SimpleURLClassLoader() { 15 //设置ClassLoader加载的路径 16 super(getMyURLs()); 17 } 18 19 private static URL[] getMyURLs(){ 20 URL url = null; 21 try { 22 url = new File(projectClassPath).toURI().toURL(); 23 } catch (MalformedURLException e) { 24 e.printStackTrace(); 25 } 26 return new URL[] { url }; 27 } 28 29 public Class load(String name) throws Exception{ 30 return loadClass(name); 31 } 32 33 public Class<?> loadClass(String name) throws ClassNotFoundException { 34 return loadClass(name,false); 35 } 36 37 /** 38 * 重写loadClass,不采用双亲委托机制("java."开头的类还是会由系统默认ClassLoader加载) 39 */ 40 @Override 41 public Class<?> loadClass(String name,boolean resolve) throws ClassNotFoundException { 42 Class clazz = null; 43 //查看HotSwapURLClassLoader实例缓存下,是否已经加载过class 44 clazz = findLoadedClass(name); 45 if (clazz != null ) { 46 if (resolve){ 47 resolveClass(clazz); 48 } 49 return (clazz); 50 } 51 52 //如果类的包名为"java."开始,则有系统默认加载器AppClassLoader加载 53 if(name.startsWith("java.")){ 54 try { 55 //得到系统默认的加载cl,即AppClassLoader 56 ClassLoader system = ClassLoader.getSystemClassLoader(); 57 clazz = system.loadClass(name); 58 if (clazz != null) { 59 if (resolve) 60 resolveClass(clazz); 61 return (clazz); 62 } 63 } catch (ClassNotFoundException e) { 64 // Ignore 65 } 66 } 67 68 return customLoad(name,this); 69 } 70 71 /** 72 * 自定义加载 73 * @param name 74 * @param cl 75 * @return 76 * @throws ClassNotFoundException 77 */ 78 public Class customLoad(String name,ClassLoader cl) throws ClassNotFoundException { 79 return customLoad(name, false,cl); 80 } 81 82 /** 83 * 自定义加载 84 * @param name 85 * @param resolve 86 * @return 87 * @throws ClassNotFoundException 88 */ 89 public Class customLoad(String name, boolean resolve,ClassLoader cl) 90 throws ClassNotFoundException { 91 //findClass(新闻热点
疑难解答