在J2EE的项目中,容器给我们提供的热部署功能使得我们不用重启动容器而修改我们的代码。比如使用Weblogic,我们可以在Weblogic-application.xml中配置是否支持热部署Servlet。查阅Weblogc 文档,其实在Weblogic中,EJB组件也是可以热部署的,但如果要热部署EJB组件,Weblogc要求必须自定义ClassLoder。 |
(3)为了安全性,JVM加载采用了双亲委派机制,如何理解呢,就是当需要加载一个类时,当前的ClassLoader先请求父ClassLoader,依次
类推,直到父类的ClassLoader无法加载时,才通过当前的ClassLoser加载,这就保证了像String这样的类型必须使用JRE里面的, 使得JRE lib 下的类不会被修改。同时避免了ClassCaseException。
(4)在JVM中,一个实例是通过本身的类名+加载它的ClassLoader识别的,也就是说 不同的ClassLoader 加载同一个类在JVM是不同的。
(5)同一个ClassLoader是不允许多次加载一个类的,否则会报java.lang.LinkageError。attempted duplicate class definition for name XXX,在下面的例子中会指出。
既然JVM不支持热部署,那么要实现热部署,就必须自定义ClassLoader,当类被修改过后,加载该类。下面通过代码说明:
// 自定义一个类加载器 public class DynamicClassLoader extends ClassLoader { public Class<?> findClass(byte[] b) throws ClassNotFoundException { return defineClass(null, b, 0, b.length); }
defineClass方法会把class的byte数组加载起来
import java.io.*; public class ManageClassLoader { DynamicClassLoader dc =null; Long lastModified = 0l; Class c = null; //加载类, 如果类文件修改过加载,如果没有修改,返回当前的 public Class loadClass(String name) throws ClassNotFoundException, IOException{ if (isClassModified(name)){ dc = new DynamicClassLoader(); return c = dc.findClass(getBytes(name)); } return c; } //判断是否被修改过 private boolean isClassModified(String filename) { boolean returnValue = false; File file = new File(filename); if (file.lastModified() > lastModified) { returnValue = true; } return returnValue; } // 从本地读取文件 private byte[] getBytes(String filename) throws IOException { File file = new File(filename); long len = file.length(); lastModified = file.lastModified(); byte raw[] = new byte[(int) len]; FileInputStream fin = new FileInputStream(file); int r = fin.read(raw); if (r != len) { throw new IOException("Can't read all, " + r + " != " + len); } fin.close(); return raw; } }
测试类;Main 每隔 5s 加载一次
import java.io.*; import java.lang.reflect.*; public class Main { /** * @param args the command line arguments */ public static void main(String[] args) throws ClassNotFoundException, IOException, NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException, InterruptedException { String path = "c:\\java\\LocalClass.class"; ManageClassLoader mc = new ManageClassLoader(); while(true){ Class c = mc.loadClass(path); Object o = c.newInstance(); Method m = c.getMethod("getName"); m.invoke(o); System.out.println(c.getClassLoader()); Thread.sleep(5000); } }
被加载的类
public class LocalClass { public void getName() { System.out.println("hahaha "); } }
运行时,每隔5s 输出:
hahaha
classloader.DynamicClassLoader@61de33
当我们修改 System.out.println( " hahaha " ); ---> System.out.println( " changed " ); 编译LocalClass后
输出变为:
changed
classloader.DynamicClassLoader@173a10f
在loadClass 中, 我们必须重新初始化一个ClassLoader,否则就会违背同一个ClassLoader是不允许多次加载一个类的。
当然,容器的实现机制肯定要完善,不可能周期性的加载,可能回通过监听机制,动态加载修改过的类。但它的实现机制肯定也是重新 实例化一个ClassLoder,加载需要加载的类。