八、类加载器与热部署
目录一、类加载简介1、类加载机制2、连接过程3、初始化二、静态代码块初始化三、一、类加载简介每个编写的”.java”拓展名类文件都存储着需要执行的程序逻辑,这些”.java”文件经过Java编译器编译成拓展名为”.class”的文件,”.class”文件中保存着Java代码经转换后的虚拟机指令当需要使用某个类时,虚拟机将会加载它的”.class”文件,并创建对应的class对象,将class文件加
·
目录
一、类加载简介
- 每个编写的”.java”拓展名类文件都存储着需要执行的程序逻辑,这些”.java”文件经过Java编译器编译成拓展名为”.class”的文件,”.class”文件中保存着Java代码经转换后的虚拟机指令
- 当需要使用某个类时,虚拟机将会加载它的”.class”文件,并创建对应的class对象,将class文件加载到虚拟机的内存,这个过程称为类加载
1、类加载机制
- 将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区中的运行时数据结构,在堆中生成一个代表这个类的java.lang.Class对象,作为方法区类数据的访问入口,这个过程需要类加载器参与
- 当系统运行时,类加载器将.class文件的二进制数据从外部存储器(如光盘,硬盘)调入内存中,CPU再从内存中读取指令和数据进行运算,并将运算结果存入内存中。内存在该过程中充当着"二传手"的作用,通俗的讲,如果没有内存,类加载器从外部存储设备调入.class文件二进制数据直接给CPU处理,而由于CPU的处理速度远远大于调入数据的速度,容易造成数据的脱节,所以需要内存起缓冲作用。
- 类将.class文件加载至运行时的方法区后,会在堆中创建一个Java.lang.Class对象,用来封装类位于方法区内的数据结构,该Class对象是在加载类的过程中创建的,每个类都对应有一个Class类型的对象,Class类的构造方法是私有的,只有JVM能够创建。因此Class对象是反射的入口,使用该对象就可以获得目标类所关联的.class文件中具体的数据结构
- 类加载的最终产物就是位于堆中的Class对象(注意不是目标类对象),该对象封装了类在方法区中的数据结构,并且向用户提供了访问方法区数据结构的接口,即Java反射的接口。
2、连接过程
- 将java类的二进制代码合并到JVM的运行状态之中的过程
- 验证:确保加载的类信息符合JVM规范,没有安全方面的问题
- 准备:正式为类变量(static变量)分配内存并设置类变量初始值的阶段,这些内存都将在方法区中进行分配
- 解析:虚拟机常量池的符号引用替换为字节引用过程
3、初始化
- 初始化阶段是执行类构造器()方法的过程。类构造器()方法是由编译器自动收藏类中的所有类变量的赋值动作和静态语句块(static块)中的语句合并产生,代码从上往下执行。
- 当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化
- 虚拟机会保证一个类的()方法在多线程环境中被正确加锁和同步
- 当范围一个Java类的静态域时,只有真正声名这个域的类才会被初始化
二、静态代码块初始化
- 对比以下2段代码,只是
private static int age = 500;
的位置不同- 因为初始化的时候静态的代码是从上往下执行的
public class App {
private static int age = 500;
static {
age = 700;
System.out.println("2静态代码块初始化");
}
public App(){
System.out.println("1构造函数");
}
public static void main(String[] args) {
App app = new App();
System.out.println(app.age);
}
}
/*
2静态代码块初始化
1构造函数
700
*/
public class App {
static {
age = 700;
System.out.println("2静态代码块初始化");
}
private static int age = 500;
public App(){
System.out.println("1构造函数");
}
public static void main(String[] args) {
App app = new App();
System.out.println(app.age);
}
}
/*
2静态代码块初始化
1构造函数
500
*/
三、热部署简介
1、热部署概念
- 对于Java应用程序来说,热部署就是在运行时更新Java类文件
2、java类的加载过程
- 首先通过java编译器,将java文件编译成class字节码,类加载器读取class字节码,再将类转化为实例,对实例newInstance就可以生成对象
- 类加载器ClassLoader功能,也就是将class字节码转换到类的实例
- 在java应用中,所有的实例都是由类加载器,加载而来。一般在系统中,类的加载都是由系统自带的类加载器完成,而且对于同一个全限定名的java类(如com.csiar.soc.HelloWorld),只能被加载一次,而且无法被卸载
- 我们可以自定义类加载器,并重写ClassLoader的findClass方法
3、热部署的三个步骤
- a)销毁该自定义ClassLoader
- b)更新class类文件
- c)创建新的ClassLoader去加载更新后的class类文件
四、热部署实现
- 基于之前说的类加载器只能加载一次,这里自定义类加载器,重新加载需要修改的class文件
- 将User中打印的信息修改为“我是User2.0版本”,然后将target目录下的User.class复制到路径D:\test“”
1、User类
public class User {
public void add(){
System.out.println("我是User1.0版本");
}
}
2、MyClassLoader
public class MyClassLoader extends ClassLoader {
//传递一个class路径地址,包名
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
//1.获取文件名称
String findName = name.substring(name.lastIndexOf(".") + 1) + ".class";
//2.读取文件流
InputStream is = this.getClass().getResourceAsStream(findName);
//3.读取字节
byte[] bytes = new byte[is.available()];
is.read(bytes);
//4.将读取byte数组给jvm识别class对象
return defineClass(name,bytes,0,bytes.length);
}catch (Exception e){
throw new ClassNotFoundException();
}
}
}
3、HotSwap
//热部署程序入口
public class HotSwap {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, InterruptedException {
System.out.println("开始加载1.0版本");
loadUser();
System.gc();//让GC回收下对象
//需要被修改的class文件
File file1 = new File("D:\\test\\User.class");
//之前的class文件
File file2 = new File("D:\\test\\study_test\\target\\classes\\com\\sjyl\\hotswap\\User.class");
//删除之前的class文件
boolean isDelete = file2.delete();
if(!isDelete){
System.out.println("热部署失败,无法删除class文件");
return;
}
file1.renameTo(file2);//移动到file2目录
System.out.println("开始加载2.0版本");
loadUser();
}
public static void loadUser() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
MyClassLoader myClassLoader = new MyClassLoader();
//使用类加载器读取信息
Class<?> findClass = myClassLoader.findClass("com.sjyl.hotswap.User");
//使用反射机制初始化对象
Object object = findClass.newInstance();
//使用反射机制调用方法
Method method = findClass.getMethod("add");
method.invoke(object);
}
}
更多推荐
已为社区贡献4条内容
所有评论(0)