佛山网站建设的设计原则,wordpress可以建网站吗,购物网站建设规划书范文,农副产品交易平台Java类加载机制 类加载器类加载器的执行流程类加载器的种类加载器之间的关系ClassLoader 的主要方法Class.forName()与ClassLoader.loadClass()区别 双亲委派模型双亲委派 类加载流程优缺点 热部署简单示例 类加载器
类加载器的执行流程 类加载器的种类
AppClassLoader 应用类… Java类加载机制 类加载器类加载器的执行流程类加载器的种类加载器之间的关系ClassLoader 的主要方法Class.forName()与ClassLoader.loadClass()区别 双亲委派模型双亲委派 类加载流程优缺点 热部署简单示例 类加载器
类加载器的执行流程 类加载器的种类
AppClassLoader 应用类加载器默认的系统类加载器负责加载java应用种classpath中的类 设置 classpath java -cp D:\aaa Main.class 获取 classpath System.getProperty(“java.class.path”) ExtClasLoader 扩展类加载器负责加载扩展目录中的java类。 设置扩展目录java -Djava.ext.dirs“D:\aa” Main.class 获取扩展目录System.getProperty(“java.ext.dirs”) 从java9开始扩展机制被移除加载器被PlatFormClassLoader取代
BootStrapClassLoader 启动类加载器负责加载JDK核心类库/jre/lib 由 C/C 语言编写无法通过java代码打印
用户自定义的ClassLoader 继承抽象类 ClassLoader 的自定义类加载器
加载器之间的关系 从JVM的角度来看一共有两种类加载器BootStrapClassLoader 和 java 自定义的类加载器 其中 BootStrapClassLoader 是使用C/C语言编写的 最高级别的 类加载器
Java 自定义的类加载器 都继承于抽象类 ClassLoader 并且内部有一个parent 属性指向父加载器是组合不是继承 ExtClassLoader 的 parent属性 是null实际是通过native方法 关联 BootStrapClassLoader 所以ExtClassLoader 的父类加载器 是 BootStrapClassLoader
JDK自带的类加载器有BootStrapClassLoaderExtClassLoaderAppClassLoader 三者并不是集成关系而是组合
用户可以继承ClassLoader 来实现自己的类加载器默认的父类加载器是 AppClassLoader
ClassLoader 的主要方法
//返回该类加载器的超类加载器
public final ClassLoader getParent()//加载名称为name的类返回结果为java.lang.Class类的实例。如果找不到类
//则返回 ClassNotFoundException异常。该方法中的逻辑就是双亲委派模式的实现
public Class? loadClass(String name) throws ClassNotFoundException//查找二进制名称为name的类返回结果为java.lang.Class类的实例。
//这是一个受保护的方法JVM鼓励我们重写此方法需要自定义加载器遵循双亲委托机制
//该方法会在检查完父类加载器之后被loadClass()方法调用。
protected Class? findClass(String name) throws ClassNotFoundException//根据给定的字节数组b转换为Class的实例off和len参数表示实际Class信息在byte数组中的位置和长度
//其中byte数组b是ClassLoader从外部获取的。
//这是受保护的方法只有在自定义ClassLoader子类中可以使用。
protected final Class? defineClass(String name, byte[] b,int off,int len)//链接指定的一个Java类。使用该方法可以使用类的Class对象创建完成的同时也被解析。
//链接阶段主要是对字节码进行验证
//为类变量分配内存并设置初始值同时将字节码文件中的符号引用转换为直接引用。
protected final void resolveClass(Class? c)//查找名称为name的已经被加载过的类返回结果为java.lang.Class类的实例。这个方法是final方法无法被修改。
protected final Class? findLoadedClass(String name)//它也是一个ClassLoader的实例这个字段所表示的ClassLoader也称为这个ClassLoader的双亲。
//在类加载的过程中ClassLoader可能会将某些请求交予自己的双亲处理。
private final ClassLoader parent;Class.forName()与ClassLoader.loadClass()区别
public static Class? forName(String className) 是类方法默认使用系统类加载器进行加载加载一个指定的类会对类进行初始化执行类中的静态代码块以及对静态变量的赋值等操作, 一般用于加载驱动例如jdbc驱动 public Class? loadClass(String name) 是成员方法懒加载只是加载不会解析更不会初始化所反射的类 双亲委派模型
rents Delegation Model 注意双亲并不是指存在父母两个类加载器实际只有一个parent 父加载器 并且是作为加载器的属性而不是继承可以理解为 雌雄同株 双亲委派 类加载流程
当一个类开始进行加载时会先从判断这个类是否已经被加载了如果已经加载了返回已加载的类Class对象如果还没有被加载通过parent属性 将加载请求传递给上层类加载器进行加载一直调用到扩展类加载器ExtClassLoader如果都没有找到已经被加载的Class对象此时parent null 通过 findBootstrapClassOrNull() 方法传递给 BootStrapClassLoader 进行类加载如果BootStrapClassLoader 没有加载成功其下层类加载器开始尝试进行加载如果一直到最底层的类加载器用户自定义的类加载器都没有加载成功则抛出ClassNotFoundException异常
一句话概括子类加载器调用父类加载器去加载类
源码如下
protected Class? loadClass(String name, boolean resolve) throws ClassNotFoundException
{synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loadedClass? c findLoadedClass(name);if (c null) {long t0 System.nanoTime();try {if (parent ! null) {// 如果有parent 交给parent 类加载器进行加载c parent.loadClass(name, false);} else {//没有parent通过native方法委派给BootStrapClassLoader 进行加载c findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {// ClassNotFoundException thrown if class not found// from the non-null parent class loader}if (c null) {//如果父类没有加载出Class对象开始尝试自己加载long t1 System.nanoTime();//根据类的全限定名获取Class 对象分为两步//1.根据类的全限定名获取字节码对象//2. 根据字节码的二进制流调用defineClass()方法生成Class对象c findClass(name);// this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;}
}优缺点
优点
防止重复加载确保一个类的全局唯一性 当一个类加载器要加载一个类首先交给父类加载器进行加载并且加载过程由syncronized锁控制。避免重复加载同一个类 确保JVM的安全防止用户用自己编写的类替换核心类库中的类 例如用户自定义一个java.lang.String的类通过双亲委派模型加载的还是核心类库中的String类不会被用户自己定义的String类替换 缺点 父类加载器不能访问子类加载器加载的类有时需要打破双亲委派模型例如Tomcat
热部署简单示例
public class MyClassLoader extends ClassLoader{/*** 文件路径*/final private String path;protected MyClassLoader(String path) {this.path path;}/*** 重写findClass实现自己的类加载器* 1. 获取class文件的字节流* 2. 调用defineClass 将字节流 转化为 CLass 对象* param name* return* throws ClassNotFoundException*/Overrideprotected Class? findClass(String name) throws ClassNotFoundException {try {// 读取字节码的二进制流byte[] b loadClassFromFile(name);// 调用 defineClass() 方法创建 Class 对象Class? c defineClass(name, b, 0, b.length);return c;} catch (IOException e) {throw new ClassNotFoundException(name);}}/*** 如果要遵循双亲委派模型则不用重写loadClass方法* param name* return* throws ClassNotFoundException*/Overridepublic Class? loadClass(String name) throws ClassNotFoundException {//为了测试方便这里简单打破下双亲委派模型先从自定义类加载器进行加载//如果不想打破双亲委派模型path路径可以设置非classpath下的路径手动复制class文件到对应目录下synchronized (getClassLoadingLock(name)) {Class? c findLoadedClass(name);if (c null) {//先从自定义类加载器进行加载这里打破了双亲委派模型try {c findClass(name);} catch (ClassNotFoundException ignored) {}//自定义类加载器加载失败交给父 类加载器if(c null){return super.loadClass(name);}}return c;}}private byte[] loadClassFromFile(String name) throws IOException {String fileName name.replace(., File.separatorChar) .class;String filePath this.path File.separatorChar fileName;try (InputStream inputStream new FileInputStream(filePath);ByteArrayOutputStream byteStream new ByteArrayOutputStream()) {int nextValue;while ((nextValue inputStream.read()) ! -1) {byteStream.write(nextValue);}return byteStream.toByteArray();}}
}public class ApplicationConfig {public void getConfig(){System.out.println(这是我的配置...);}}public class HotDeploymentTest {public static void main(String[] args) throws Exception{Scanner sc new Scanner(System.in);while(true){MyClassLoader loader new MyClassLoader(E:\\Work\\classloader\\target\\classes);Class? aClass loader.loadClass(cn.rwto.sample.ApplicationConfig);Object applicationConfig aClass.newInstance();Method method aClass.getMethod(getConfig);method.invoke(applicationConfig);Thread.sleep(5000);}}
}在不关闭进程的情况下修改代码重新编译控制台发现 打印的内容跟着改变热部署实现完毕