热部署
对于Java应用程序来说,热部署就是在运行时更新Java类文件。
热部署的原理是什么
想要知道热部署的原理,必须要了解java类的加载过程。一个java类文件到虚拟机里的对象,要经过如下过程。
首先通过java编译器,将java文件编译成class字节码,类加载器读取class字节码,再将类转化为实例,对实例newInstance就可以生成对象。
类加载器ClassLoader功能,也就是将class字节码转换到类的实例。
在java应用中,所有的实例都是由类加载器,加载而来。
一般在系统中,类的加载都是由系统自带的类加载器完成,而且对于同一个全限定名的java类(如com.csiar.soc.HelloWorld),只能被加载一次,而且无法被卸载。
这个时候问题就来了,如果我们希望将java类卸载,并且替换更新版本的java类,该怎么做呢?
既然在类加载器中,java类只能被加载一次,并且无法卸载。那是不是可以直接把类加载器给换了?答案是可以的,我们可以自定义类加载器,并重写ClassLoader的findClass方法。想要实现热部署可以分以下三个步骤:
1、销毁该自定义ClassLoader
2、更新class类文件
3、创建新的ClassLoader去加载更新后的class类文件。
热部署与热加载
Java热部署与Java热加载的联系和区别
Java热部署与热加载的联系
1.不重启服务器编译/部署项目
2.基于Java的类加载器实现
Java热部署与热加载的区别
部署方式
热部署在服务器运行时重新部署项目
热加载在运行时重新加载class
实现原理
热部署直接重新加载整个应用
热加载在运行时重新加载class
使用场景
热部署更多的是在生产环境使用
热加载则更多的实在开发环境使用
V2.class 覆盖 V1.class文件 会直接被加载吗?不会呀 重启后就可以加载新的了! 只会读取一次
自定义ClassLoader
package com.toov5.test; import java.io.IOException; import java.io.InputStream; /* * 穿class文件路径地址然后 将其读入jvm中去 * */ public class MyClassLoader extends ClassLoader { @Override protected Class<?> findClass(String name) throws ClassNotFoundException { try { //1获取文件名称 String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class"; //2、 文件名称 InputStream iStream =this.getClass().getResourceAsStream(fileName); //3、读取字节 byte[] bytes; bytes = new byte[iStream.available()]; iStream.read(bytes); //3、将读取Byte数组给jvm识别Class对象 return defineClass(name, bytes, 0, bytes.length); } catch (IOException e) { throw new ClassNotFoundException(); } } }
自定义一个类:
package com.toov5.test; public class User { public void add(){ System.out.println("我是版本1"); } }
实现:
package com.toov5.test; import java.lang.reflect.Method; public class Hotswap { public static void main(String[] args) throws InterruptedException, ClassNotFoundException { loadUser(); } public static void loadUser() throws ClassNotFoundException{ //专门加载类信息 MyClassLoader myClassLoader= new MyClassLoader(); //使用类加载器读取信息 Class<?> findClass = myClassLoader.findClass("com.toov5.test.User"); try { //反射初始化对象 Object object = findClass.newInstance(); //反射调用方法 Method method = findClass.getMethod("add"); method.invoke(object); System.out.println(object.getClass()); System.out.println(object.getClass().getClassLoader()); } catch (Exception e) { } } }
可以设计 每隔多久调用一次这个自自定义的类加载器方法~~~
关于类加载器
Java程序有个类:
System jvm首先把类的字节码 加载到内存 这个.class放在 classPath下磁盘的某个路径下 这都是类加载器干的
Java虚拟机中有多个类加载器,系统默认的三个主要的类加载器,每个负责加载特定位置的类:
BootStrap ExtClassLoader AppClassLoader
类加载器 也是个Java类,因为其他是Java类的类加载器本身也要被类加载器加载,显然必须有一个类加载器不是Java类,这个就是BootStrap(是C++的)
Java虚拟机中的所有类加载器采用具有父子关系的树形结构进行组织,在实例化每个类加载器对象时,需要为其指定一个父级类装载器对象或者默认采用系统类装载器为期父类加载。
public class testClassLoader { public static void main(String[] args) { //得到这个类的类加载器的类的名字 类加载器属于一个类 String name = testClassLoader.class.getClassLoader().getClass().getName(); System.out.println(name); } }
结果:
看下System:
public class testClassLoader { public static void main(String[] args) { //得到这个类的类加载器的类的名字 类加载器属于一个类 String name1 = System.class.getClassLoader().getClass().getName(); System.out.println(name1); } }
空指针异常了
特殊的类加载器 不是Java类
只要是Class对象一定是由类加载器加载出来的 不可能不存在
这个可能性是在跟前面
System.class.getClassLoader()
没有这个对象
是因为特殊类加载器 C++写的那个。用Java程序去获得这个名字就是 Null了
循环打印下:
public class testClassLoader { public static void main(String[] args) { ClassLoader classLoader = testClassLoader.class.getClassLoader(); while ( classLoader != null){ System.out.println(classLoader.getClass().getName()); classLoader = classLoader.getParent(); } System.out.println(classLoader); } }
如果有两份,那么加载父类包下面的哦!
要想自己写类加载器,需要挂到爸爸下面。
当Java虚拟机要加载一个类时候,到底派哪个类加载器去加载?
首先当前线程的类加载器去加载线程中的第一个类
如果类A中引用了类B, Java虚拟机将使用加载类A的类加载器来加载类B
还可以直接调用ClassLoader.loadClass() 方法来指定某个类加载器去加载某个类
每个类加载器加载类时候,现委托给上级类加载器
当所有祖宗类的加载器没有加载到类,回到发起者类加载器,还加载不了,则抛出ClassNotFoundException,不是再去找发起者类加载器的儿子,因为没有getChild方法,即便是有,如果多个儿子,找哪个呢?
每个ClassLoader本身只能分别加载特定位置和目录中的类,但他们可以委托其他的类加载器去加载类,这就是类加载器的委托机制。类加载器一级一级委托到BootStrap类加载器,当BootStrap无法加载当前所需要加载的类时候,然后才一级一级回退到子孙类加载器进行真正的加载。当退回到最初的类加载器时候,如果它自己也不能完成类的加载,那就应报告ClassNotFountException异常
自定义类加载器:
待续