`
y806839048
  • 浏览: 1126090 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

JVM——自定义类加载器

阅读更多

总括:

不同的加载器加载不同的目录下的文件,需要哪个类被哪个加载器加载只要在哪个目录下放置即可

 

当为了防止反编译对非常规的加密的字节码加载,此时需要自定义加载器先解密,然后正常加载---此时用就自定义加载器

 

 

loadClass=findClass(包含defineClass)

findclass中通过类的路劲获取到类的字节码(getclass),然后defineClass根据字节码和类的名字返回类的实例

 

一般是loadClass先加载,找不到父类加载器就用findclass加载(找不到父类加载器的方法????)

一般是直接自定义的加载器对象指定要加载类的路径,然后这个加载器对象加载这个名字的类

 

参考:

https://blog.csdn.net/qiyue683209/article/details/82940

 

0. 为什么需要自定义类加载器  

网上的大部分自定义类加载器文章,几乎都是贴一段实现代码,然后分析一两句自定义ClassLoader的原理。但是我觉得首先得把为什么需要自定义加载器这个问题搞清楚,因为如果不明白它的作用的情况下,还要去学习它显然是很让人困惑的。

首先介绍自定义类的应用场景:

(1)加密:Java代码可以轻易的被反编译,如果你需要把自己的代码进行加密以防止反编译,可以先将编译后的代码用某种加密算法加密,类加密后就不能再用Java的ClassLoader去加载类了,这时就需要自定义ClassLoader在加载类的时候先解密类,然后再加载。

(2)从非标准的来源加载代码:如果你的字节码是放在数据库、甚至是在云端,就可以自定义类加载器,从指定的来源加载类

(3)以上两种情况在实际中的综合运用:比如你的应用需要通过网络来传输 Java 类的字节码,为了安全性,这些字节码经过了加密处理。这个时候你就需要自定义类加载器从某个网络地址上读取加密后的字节代码,接着进行解密和验证,最后定义出在Java虚拟机中运行的类。

1. 双亲委派模型

在实现自己的ClassLoader之前,我们先了解一下系统是如何加载类的,那么就不得不介绍双亲委派模型的实现过程。

复制代码
//双亲委派模型的工作过程源码
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{
    // First, check if the class has already been loaded
    Class c = findLoadedClass(name);
    if (c == null) {
        try {
            if (parent != null) {
                c = parent.loadClass(name, false);
            } else {
                c = findBootstrapClassOrNull(name);
            }
        } 
        catch (ClassNotFoundException e) {
            // ClassNotFoundException thrown if class not found
            // from the non-null parent class loader
            //父类加载器无法完成类加载请求
        }
 
        if (c == null) {
            // If still not found, then invoke findClass in order to find the class
            //子加载器进行类加载 
            c = findClass(name);
        }
    }
 
    if (resolve) {
        //判断是否需要链接过程,参数传入
        resolveClass(c);
    }
 
    return c;
}
复制代码

双亲委派模型的工作过程如下:

(1)当前类加载器从自己已经加载的类中查询是否此类已经加载,如果已经加载则直接返回原来已经加载的类。

(2)如果没有找到,就去委托父类加载器去加载(如代码c = parent.loadClass(name, false)所示)。父类加载器也会采用同样的策略,查看自己已经加载过的类中是否包含这个类,有就返回,没有就委托父类的父类去加载,一直到启动类加载器。因为如果父加载器为空了,就代表使用启动类加载器作为父加载器去加载。

(3)如果启动类加载器加载失败(例如在$JAVA_HOME/jre/lib里未查找到该class),则会抛出一个异常ClassNotFoundException,然后再调用当前加载器的findClass()方法进行加载

双亲委派模型的好处:

(1)主要是为了安全性,避免用户自己编写的类动态替换 Java的一些核心类,比如 String。

(2)同时也避免了类的重复加载,因为 JVM中区分不同类,不仅仅是根据类名,相同的 class文件被不同的 ClassLoader加载就是不同的两个类。

2. 自定义类加载器

(1)从上面源码看出,调用loadClass时会先根据委派模型在父加载器中加载,如果加载失败,则会调用当前加载器的findClass来完成加载

(2)因此我们自定义的类加载器只需要继承ClassLoader,并覆盖findClass方法,下面是一个实际例子,在该例中我们用自定义的类加载器去加载我们事先准备好的class文件。

2.1 自定义一个People.java类做例子

复制代码
public class People {
//该类写在记事本里,在用javac命令行编译成class文件,放在d盘根目录下
    private String name;
 
    public People() {}
 
    public People(String name) {
        this.name = name;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public String toString() {
        return "I am a people, my name is " + name;
    }
 
}
复制代码

2.2 自定义类加载器

自定义一个类加载器,需要继承ClassLoader类,并实现findClass方法。其中defineClass方法可以把二进制流字节组成的文件转换为一个java.lang.Class(只要二进制字节流的内容符合Class文件规范)。

复制代码
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.WritableByteChannel;
 
public class MyClassLoader extends ClassLoader
{
    public MyClassLoader()
    {
        
    }
    
    public MyClassLoader(ClassLoader parent)
    {
        super(parent);
    }
    
    protected Class<?> findClass(String name) throws ClassNotFoundException
    {
        File file = new File("D:/People.class");
        try{
            byte[] bytes = getClassBytes(file);
            //defineClass方法可以把二进制流字节组成的文件转换为一个java.lang.Class
            Class<?> c = this.defineClass(name, bytes, 0, bytes.length);
            return c;
        } 
        catch (Exception e)
        {
            e.printStackTrace();
        }
        
        return super.findClass(name);
    }
    
    private byte[] getClassBytes(File file) throws Exception
    {
        // 这里要读入.class的字节,因此要使用字节流
        FileInputStream fis = new FileInputStream(file);
        FileChannel fc = fis.getChannel();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        WritableByteChannel wbc = Channels.newChannel(baos);
        ByteBuffer by = ByteBuffer.allocate(1024);
        
        while (true){
            int i = fc.read(by);
            if (i == 0 || i == -1)
            break;
            by.flip();
            wbc.write(by);
            by.clear();
        }
        fis.close();
        return baos.toByteArray();
    }
}
 
复制代码

2.3 在主函数里使用

MyClassLoader mcl = new MyClassLoader(); 
Class<?> clazz = Class.forName("People", true, mcl); 
Object obj = clazz.newInstance();
       
System.out.println(obj);
System.out.println(obj.getClass().getClassLoader());//打印出我们的自定义类加载器

2.4 运行结果

分享到:
评论

相关推荐

    Java之——类热加载

    传统的Java应用程序在启动时,由JVM(Java虚拟机)通过类加载器将类加载到内存中,一旦加载完成,除非程序退出,否则这些类通常不会被重新加载。然而,在开发过程中,我们可能希望在不重启应用的情况下,对已加载的...

    深入java虚拟机(六)——类加载的父亲委托机制1

    4. **用户自定义类加载器**:开发者可以根据需求创建自己的类加载器,它们可以继承自`java.lang.ClassLoader`抽象类。 父亲委托机制的工作流程如下: 1. 当需要加载一个类时,首先由当前类加载器询问其父类加载器...

    【JVM】类的奇幻漂流——类加载机制探秘

    在实际编程中,了解这些概念有助于我们理解和优化程序性能,尤其是在涉及类加载策略、类加载器自定义以及类的生命周期管理时。通过实例和实践,我们可以更深入地掌握这些概念,提升我们的JVM调优技能。

    坚持写博客第一周--java基础知识回顾--jvm类加载1(csdn)————程序.pdf

    Java虚拟机(JVM)的类加载机制是Java运行时环境的重要组成...总之,JVM的类加载机制是Java运行时的关键部分,它保证了程序的稳定性和安全性,同时也提供了灵活性,使得我们可以自定义加载逻辑,满足特定场景的需求。

    JVM学习笔记(一)——类的加载机制

    - **自定义类加载器**:开发者可以根据需求创建自己的类加载器,实现特定的加载策略。 3. **双亲委派模型** 类加载机制遵循双亲委派模型,即当一个类加载器收到加载类的请求时,它首先会委托父类加载器尝试加载,...

    JVM.zip_JVM代理_classloader

    而JVM代理(JVM Agent)和类加载器(ClassLoader)则是两个关键的概念,它们对于深入理解Java应用程序的运行机制至关重要。 **JVM代理** JVM代理是一种在JVM启动时或者运行时插入额外功能的方式,它允许开发者在不...

    JVM解毒——类加载子系统

    什么是类加载器,类加载器有哪些? 多线程的情况下,类的加载为什么不会出现重复加载的情况? 什么是双亲委派机制?它有啥优势?可以打破这种机制吗? 类加载子系统 类加载机制概念 Java虚拟机把描述类的数据从...

    tomcat 类加载机制 —— ClassLoader

    在Tomcat中,我们可以通过配置`catalina.properties`文件和`server.xml`文件来调整类加载策略,例如设置自定义的类加载顺序或启用共享类加载器。 此外,Tomcat还支持热部署,即在不重启服务器的情况下更新Web应用的...

    Java反射机制——类的加载方法,创建对象,获取方法以及结构

    - **方式四**:利用类加载器`ClassLoader`加载,这通常用于自定义加载机制。 4. **类加载器** - **Bootstrap ClassLoader**:引导类加载器,加载JDK的核心类库,如`rt.jar`。 - **Extension ClassLoader**:扩展...

    类加载说明.pdf

    本节介绍了一种特殊的类加载器——编译时类加载器。这类加载器能够在加载类之前自动编译源代码。这对于动态语言或需要即时编译的场景非常有用,比如当开发者希望在运行时修改某些行为而不需要重新编译整个项目时。 ...

    Java类加载原理解析文档

    此外,还有一个特殊类型的类加载器——线程上下文类加载器,稍后会单独讨论。 类加载器之间遵循双亲委派机制。这意味着当一个类加载器收到加载类的请求时,它会先委托给其父类加载器尝试加载。只有当父类加载器无法...

    实战Java虚拟机——JVM故障诊断与性能优化.pdf

    书中可能讲解了双亲委派模型、自定义类加载器以及类加载器之间的关系。 8. **性能优化实践**:除了理论知识,书中可能还提供了大量实战案例,如通过代码优化减少对象创建、利用String池提高效率、避免过多反射操作...

    深入java虚拟机(二)——类的生命周期(上)类的加载和连接1

    此外,用户还可以自定义类加载器,实现特定的加载逻辑。 类的生命周期还包括使用和卸载阶段。一旦类被加载并初始化,就可以在程序中使用。而当JVM不再需要一个类时,可能会进行卸载,但这通常发生在系统资源极度...

    2015-11-ClassLoaders-Selajev.pdf

    Java类加载器是Java虚拟机(JVM)的核心组件之一,它负责将Java类加载到JVM中以供运行。Java类是Java类型系统的构建块,但它们还有一个基本目的:类是一个编译单元,是可以在JVM进程中单独加载和运行的最小代码单元...

    Java ClassLoader Tutorial.zip

    Java 类加载器(ClassLoader)是Java虚拟机(JVM)的重要组成部分,它负责在运行时查找并加载类到JVM中。这个教程将深入探讨ClassLoader的工作原理、类型以及如何自定义类加载器。 一、Java ClassLoader 基础 1. 类...

    JVM底层原理课件PPT

    双亲委派模型是JVM中类加载器之间的关系,每个类加载器都有一个父加载器,除了启动类加载器外,其他类加载器都有自己的父类加载器。当某个特定的类加载器接收到加载请求时,首先将加载任务委托给父类加载器,只有...

    ContextClassLoaderExample:一个非常简单的例子来展示你如何定义一个拦截类加载器来获取关于哪些类已经加载的通知

    1. 创建自定义类加载器:定义一个新的类加载器类,继承自`ClassLoader`,并实现类加载的逻辑。 2. 设置线程上下文类加载器:在主线程或特定线程中,使用`Thread.currentThread().setContextClassLoader()`方法设置...

    Jave深度历险(CH_02深入类别载入器)

    例如,在企业级应用服务器中,为了支持热部署(Hot Deployment)——即在服务器不停机的情况下更新服务组件,通常会自定义类加载器。通过自定义类加载器,开发者能够精确控制类的加载时机、来源以及加载策略,实现更为...

    2021Java字节跳动面试题——面向字节_JVM(下).pdf

    5. **初始化**:这是类加载的最后一步,在之前的类加载过程中,除了在加载阶段用户应用程序可以通过自定义类加载器参与外,其余动作完全由虚拟机主导和控制。到了初始化阶段,才真正开始执行类中定义的Java程序代码...

Global site tag (gtag.js) - Google Analytics