热部署其实就是如何在服务器不重启的时候动态的替换相关模块,要讲热部署以前就要先了解一下类加载机制。java
JVM加载一个类须要通过这么几个阶段,加载,验证,解析,初始化,使用,卸载。其中到初始化阶段以前是类的加载所有。而且JVM在加载一个类时是用的双亲委派模型。也就是当classloader加载一个类的时候并非本身去尝试加载,而是一层一层委派,当顶层父加载器没法加载,再自顶向下尝试加载。缓存
类加载器在加载一个类时,只会加载一次,也就是一个加载器下的同名类只会存在一个,每次加载都会先看缓存。因此热部署的关键就是自定义一个加载器,先继承classloader,而后实现findclass方法,这样就能绕过双亲委派模型的限制。服务器
直接上代码:每次加载一个类的时候先看一下是否被修改过,若是被修改过那么尝试加载。框架
这里有个问题,类加载器啥时候被回收,好像看起来会一直占着不会被GC。因此如今基本上都是经过OSGi框架来实现模块的热插拔,感兴趣的朋友能够参考《OSGi原理与最佳实践》。函数
package com.dlb.note.hotDeploy; import java.lang.reflect.Method; /** * 功能:主函数类,专门用于测试 * 版本:1.0 * 日期:2016/12/9 11:18 * 做者:馟苏 */ public class Main { /** * 主函数 */ public static void main(String []args) { while (true) { try { Class cls = ManageClassLoader.loadClass("E:\\note\\target\\classes\\com\\dlb\\note\\doj\\Cat.class"); Method method = cls.getMethod("cry"); method.invoke(cls.newInstance()); Thread.sleep(5000); } catch (Exception e) { System.out.println(e); } } } }
package com.dlb.note.hotDeploy; import java.io.File; import java.io.FileInputStream; import java.io.IOException; /** * 功能:管理类加载器 * 版本:1.0 * 日期:2016/12/9 10:34 * 做者:馟苏 */ public class ManageClassLoader { /**类文件的上次修改时间*/ private static Long lastModified = 0l; /**类的缓存*/ private static Class cache = null; /**动态类加载器*/ private static DynamicClassLoader dc = null; /** * 这是入口方法! * 加载类, 若是类文件修改过加载,若是没有修改,返回当前的 * @param fileName 路径名 + 文件名 + 扩展名 * @return * @throws ClassNotFoundException * @throws IOException */ public synchronized static Class loadClass(String fileName) throws ClassNotFoundException, IOException{ if (isClassModified(fileName)){ // 类文件被修改 /**建立一个动态类加载器用于加载类(同一个类加载器下不能出现两个同名类)*/ dc = new DynamicClassLoader(); return (cache = dc.findClass(getBytes(fileName))); } return cache; } /** * 判断是否被修改过 * @param fileName 路径名 + 文件名 + 扩展名 * @return */ private synchronized static boolean isClassModified(String fileName) { boolean returnValue = false; File file = new File(fileName); if (file.lastModified() > lastModified) { returnValue = true; } return returnValue; } /** * 从本地读取文件 * @param fileName 路径名 + 文件名 + 扩展名 * @return * @throws IOException */ private synchronized static 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; } public static void main(String []args) throws Exception{ DynamicClassLoader dc = new DynamicClassLoader(); Class<?> cls = dc.findClass(getBytes("E:\\note\\target\\classes\\com\\dlb\\note\\doj\\Cat.class")); System.out.println(cls); cls = dc.findClass(getBytes("E:\\note\\target\\classes\\com\\dlb\\note\\doj\\Cat.class")); System.out.println(cls); } } /** * 一个动态类加载器,用于加载类(同一个类加载器下不能出现两个同名类) */ class DynamicClassLoader extends ClassLoader { /** * 根据字节定义类 * 注意:对于用一个类加载器不能定义同一个类名两次 * @param b * @return * @throws ClassNotFoundException */ public Class<?> findClass(byte[] b) throws ClassNotFoundException { return defineClass(null, b, 0, b.length); } }