首页 > 其他分享 >JVM自定义类的加载器

JVM自定义类的加载器

时间:2024-09-27 16:20:29浏览次数:10  
标签:Java String 自定义 ClassLoader JVM new 加载

自定义类的加载器

咱们书接上回继续说说自定义类类加载器

自定义类加载器有什么用?

  • 通过类加载器可以实现非常精妙的插件机制。例如:著名的OSGI组件框架,再如Eclipse的插件机制。类加载器为应用程序提供了一种动态增加新功能的机制,这种机制无须重新打包发布应用程序就能实现。
  • 同时,自定义类加载器能够实现应用隔离。例如 Tomcat,Spring等中间件和组件框架都在内部实现了自定义的加载器,并通过自定义加载器隔离不同的组件模块。这种机制比C/C++程序要好太多,想不修改C/C++程序就能为其新增功能,几乎是不可能的,仅仅一个兼容性便能阻挡住所有美好的设想。

同时注意所有用户自定义类的加载器通常需要继承于抽象类java.lang.ClassLoader
 

说明:
每个Class对象都会包含一个定义它的ClassLoader的引用

获取ClassLoader的途径:

  • 获取当前类的 ClassLoader clazz.getClassloader()
  • 获取当前线程的上下文的 ClassLoader Thread.curentThread().getContextClassLoader()
  • 获得系统的ClassLoader ClassLoader.getSystemClassLoader()

说明:
站在程序的角度看,引导类加载器与另外两种类加载器(系统类加载器和扩展类加载器)并不是同一个层次意义上的加载器,引导类加载器是使用C++语言编写而成的,而另外两种类加载器则是使用Java语言编写而成的。由于引导类加载器压根儿就不是一个Java类,因此在Java程序中只能打印出空值

数组类的Class对象,不是由类加载器去创建的,而是在Java运行期JVM根据需要自动创建的。对于数组类的类加载器来说,是通过Class.getClassLoader()返回的,与数组当中元素类型的类加载器是一样的;如果数组当中的元素类型是基本数据类型,数组类是没有类加载器的。

为什么要自定义类的加载器?

隔离加载类

在某些框架内进行中间件与应用的模块隔离,把类加载到不同的环境。比如:阿里内某容器框架通过自定义类加载器确保应用中依赖的jar包不会影响到中间件运行时使用的jar包。再比如:Tomcat这类Web应用服务器,内部自定义了好几种类加载器,用于隔离同一个Web应用服务器上的不同应用程序。 (类的仲裁–>类冲突)

修改类加载的方式

类的加载模型并非强制,除Bootstrap外,其他的加载并非一定要引入,或者根据实际情况在某个时间点进行按需进行动态加载

扩展加载源

比如从数据库、网络、甚至是电视机机顶盒进行加载

防止源码泄漏

Java代码容易被编译和篡改,可以进行编译加密。那么类加载也需要自定义,还原加密的字节码

应用场景有哪些???

实现类似进程内隔离,类加载器实际上用作不同的命名空间,以提供类似容器、模块化的效果。例如,两个模块依赖于某个类库的不同版本,如果分别被不同的容器加载,就可以互不干扰。这个方面的集大成者是Java EE和OSGI、JPMS等框架。

应用需要从不同的数据源获取类定义信息,例如网络数据源,而不是本地文件系统。或者是需要自己操纵字节码,动态修改或者生成类型。

注意:
在一般情况下,使用不同的类加载器去加载不同的功能模块,会提高应用程序的安全性。但是,如果涉及Java类型转换,则加载器反而容易产生不美好的事情。在做Java类型转换时,只有两个类型都是由同一个加载器所加载,才能进行类型转换,否则转换时会发生异常。

 

自定义类加载器的两种实现方式:

  1. 重载 loadClass(): 这个方法是双亲委派机制模型逻辑的地方,擅自修改这个方法会导致模型破坏,容易造成问题。因此我们最好是在双亲委派模型框架内进行小范围改动,不破坏原有结构。同时,也避免了自己重写 loadClass() 方法的过程中必须写双亲委派的重复代码,从代码的复用性来看,不直接修改这个方法始终是比较好的选择
  2. 重载 findClass() 方法

 

简单的自定义类加载器的实现

public class UserDefineClassLoader extends ClassLoader {

    private String rootPath;

    public UserDefineClassLoader(String rootPath) {
        this.rootPath = rootPath;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        //转换成以文件路径表示的文件
        String filePath = classToFilePath(name);

        //指定路径的class文件对应的二进制流数据
        byte[] data = getBytesFromPath(filePath);

        //自定义classloader内部需要调用defineClass()
        return defineClass(name,data,0,data.length);
    }

    private byte[] getBytesFromPath(String filePath) {

        FileInputStream in = null;
        ByteArrayOutputStream baos = null;
        try {
            in = new FileInputStream(filePath);

            baos = new ByteArrayOutputStream();

            byte[] buffer = new byte[1024];

            int len;

            while ((len = in.read(buffer)) != -1) {
                baos.write(buffer, 0, len);
            }

            return baos.toByteArray();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (baos != null)
                    baos.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            try {
                if (in != null)
                    in.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    private String classToFilePath(String name) {

        return rootPath + "\\" + name.replace(".", "\\") + ".class";
    }

    public static void main(String[] args) throws ClassNotFoundException {
        UserDefineClassLoader loader =
                new UserDefineClassLoader("所要加载的类字节码文件所在路径");

        loader.findClass("类的全限定名");
    }
}

 
既然上面收到了双亲委派机制那我们就来看一下到底什么是双亲委派机制?

定义:

一个类加载器在接收到类加载请求时,他不会首先自己去加载这个类,而是把这个任务去委托给父类进行加载,以此递归,如果父类加载器可以完成加载任务则返回,只有在父类无法完成加载任务时才会由自己去加载

双亲委派机制的优劣:

优势:

  • 避免类的重复加载,保证类的全局唯一性
  • 保护程序安全,防止核心API被随意更改

劣势:

  • 双亲委派是单向委托的模型,虽然结构清晰,但是上层类加载器无法访问下层类加载器

结论:由于Java虚拟机规范并没有明确要求类加载器的加载机制一定要使用双亲委派模型,只是建议采用这种方式而已

今天的文章就水到这里啦!!!!!
下期再见

标签:Java,String,自定义,ClassLoader,JVM,new,加载
From: https://blog.csdn.net/qq_74212030/article/details/142595708

相关文章

  • 自动加载类文件时发生错误,类名【core\\basic\\Kernel】
    当你使用PbootCMS时遇到了自动加载类文件时发生的错误,具体错误信息如下:自动加载类文件时发生错误,类名【core\\basic\\Kernel】这个问题通常是由于Kernel.php文件丢失或被误删除导致的。特别是在阿里云虚拟主机环境下,可能会因为安全策略而删除某些文件。以下是详细的解决......
  • uniapp [全端兼容] - 详细实现拍照或相册选取图片后插入水印功能,手机拍照或相册上传图
    前言网上的教程乱七八糟且兼容性太差,本文提供优质示例。在uni-app全平台兼容(H5网页网站、支付宝/微信小程序、安卓App、苹果App、nvue)开发中,详解手机从相册选取上传图像后加入水印功能,手机拍摄照相后也可以加入水印,Uniapp给图片添加水印,获取上传或拍摄的图片信息后,为......
  • 鸿蒙(HarmonyOS)--声明式UI、自定义组件
    目录1.基础语法概述2.声明式UI描述2.1创建组件2.1.1无参数2.1.2有参数2.2配置属性2.3配置事件 2.4配置子组件3.自定义组件3.1创建自定义组件3.1.1基本使用3.1.2组件属性、方法3.1.3通用样式事件 3.2页面和自定义组件生命周期3.2.1自定义组件的创建......
  • PbootCMS默认面包屑导航样式修改及自定义的设置方法
    在使用PBootCMS建站时,如果需要对系统默认的面包屑标签进行样式修改,可以通过调整相应的参数来实现。以下是具体的步骤和示例代码:修改面包屑标签的样式自定义分隔符修改首页文本添加首页图标添加分割图标示例代码假设你需要修改面包屑标签的分隔符、首页文本以及图标,可以按......
  • 自定义 Git
    我们可以对Git做一些配置。‍配置别名有没有经常敲错命令?比如gitstatus​?status​这个单词真心不好记。如果敲gitst​就表示gitstatus​那就简单多了,当然这种偷懒的办法我们是极力赞成的。我们只需要敲一行命令,告诉Git,以后st​就表示status​:$gitconfig--......
  • hibernate 自定义表名与列名 - 增删改查分页 - 兼容Mysql和Oracle
    1.新增service,先组装SQLimportlombok.extern.slf4j.Slf4j;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.beans.factory.annotation.Value;importorg.springframework.data.domain.Page;importorg.springframework.stereo......
  • vue父子组件的生命周期加载顺序
    vue父子组件的生命周期加载顺序官网里vue组件的生命周期钩子汇总列举如下:生命周期单个vue组件的生命周期执行顺序已经非常熟悉了。但是,如果有嵌套组件,父子组件的生命周期的执行顺序是什么呢?嵌套组件又分为2种情况:一种是在template直接引入(大部分场景),另一种是element......
  • Unity UI Tookite:实现命令控制台 [自定义元素]
    目录前言功能需求基础逻辑实现——输入输出分离逻辑实现——命令解析/历史指令切换历史指令解析指令基于反射的命令组自动装载逻辑实现——命令提示逻辑实现——定位报错逻辑实现——内容滚动/元素铺满逻辑实现——可变文本块最后前言最近在将Godot项目重写至Unit......
  • 页面底部"上拉加载更多"
    [前两天发烧感冒头痛,所以没有更新,现在恢复更新了]本套程序使用uni-app,语法为vue,可以编写在多个平台上运行的程序首先我们完成最下面的"下拉刷新更多",放在底部<viewclass="d-flexa-centerj-centertext-light-mutedfont-mdpy-3"> 下拉刷新更多</view>再在给scroll-v......
  • Windows 允许用户自定义和安装网络协议。以下是一些方法和步骤,帮助您在 Windows 中进
    Windows允许用户自定义和安装网络协议。以下是一些方法和步骤,帮助您在Windows中进行此操作。1.使用设备管理器安装协议您可以通过设备管理器来安装特定的网络协议:打开设备管理器:右键点击“开始”菜单,选择“设备管理器”。找到网络适配器:展开“网络适配器”部分。......