ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Java--ClassLoader 类加载机制与重写类加载

2021-12-22 21:36:46  阅读:165  来源: 互联网

标签:Java -- TestHelloWorld 108 101 hello 97 加载


1.ClassLoader

Java是依赖JVM实现的跨平台开发,程序运行前需要先编译class文件,

Java类初始化的时候会调用java.lang.Classloader来加载字节码,

然后ClasssLoader调用JVM的native方法来定义一个java.lang.Class实例。

 

2.Java类

public class TestHello {
    public String hello(){
        return "hello,world!";
    }
}

这里编译成一个java文件

 

 使用javap -c 命令反汇编class文件

 

 JVM再执行我们的TestHello时候会先解析class的二进制内容,其实就是javap命令生成的字节码。

 

 

3.类加载方法

所有的java类都必须经过JVM加载后才能运行,ClassLoader主要作用就是用于Java类文件的加载。

在JVM类加载器中最顶层的是Bootstrap ClassLoader(引导类加载器)Extension ClassLoader(扩展类加载器)(接触较少)App ClassLoader(系统类加载器)(直接加载我们的代码)AppClassLoader是默认的类加载器,如果类加载时我们不指定类加载器的情况下,默认会使用AppClassLoader加载类。

 可以这么说 用java.io.File.class.getClassLoader()取得是null的对象,就是用Bootstrap去加载的,以为它是用C++去实现的,所以当然得不到对应的对象了!

ClassLoader类有如下核心方法:

  1. loadClass(加载指定的Java类)
  2. findClass(查找指定的Java类)
  3. findLoadedClass(查找JVM已经加载过的类)
  4. defineClass(定义一个Java类)
  5. resolveClass(链接指定的Java类)

 

4.Java动态加载方式

Java的加载方式有显式与隐式。

显式:Java反射或者ClassLoader来动态加载一个类对象。

隐式:类名.方法名()或者new()类的实例。

我们可以自定义类加载器去加载任意的类

// 反射加载TestHelloWorld示例
Class.forName("com.anbai.sec.classloader.TestHelloWorld");

// ClassLoader加载TestHelloWorld示例
this.getClass().getClassLoader().loadClass("com.anbai.sec.classloader.TestHelloWorld");

 

 

5.重写classloader

可以通过重写classloader类来加载字节码(为一个不存在的类),加载到JVM里面去,然后通过反射去调用这个类来实例化他的对象调用他的方法。这里就以TestHelloWorld类为例,先注释掉之前写的TestHelloWorld,

 

 完整重写代码

package com.anbai.sec.classloader;

import java.lang.reflect.Method;

/**
 * Creator: yz
 * Date: 2019/12/17
 */
public class TestClassLoader extends ClassLoader {

    // TestHelloWorld类名
    public static String TEST_CLASS_NAME = "com.anbai.sec.classloader.TestHelloWorld";

    // TestHelloWorld类字节码
    public static byte[] TEST_CLASS_BYTES = new byte[]{
            -54, -2, -70, -66, 0, 0, 0, 51, 0, 17, 10, 0, 4, 0, 13, 8, 0, 14, 7, 0, 15, 7, 0,
            16, 1, 0, 6, 60, 105, 110, 105, 116, 62, 1, 0, 3, 40, 41, 86, 1, 0, 4, 67, 111, 100,
            101, 1, 0, 15, 76, 105, 110, 101, 78, 117, 109, 98, 101, 114, 84, 97, 98, 108, 101,
            1, 0, 5, 104, 101, 108, 108, 111, 1, 0, 20, 40, 41, 76, 106, 97, 118, 97, 47, 108,
            97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 1, 0, 10, 83, 111, 117, 114, 99,
            101, 70, 105, 108, 101, 1, 0, 19, 84, 101, 115, 116, 72, 101, 108, 108, 111, 87, 111,
            114, 108, 100, 46, 106, 97, 118, 97, 12, 0, 5, 0, 6, 1, 0, 12, 72, 101, 108, 108, 111,
            32, 87, 111, 114, 108, 100, 126, 1, 0, 40, 99, 111, 109, 47, 97, 110, 98, 97, 105, 47,
            115, 101, 99, 47, 99, 108, 97, 115, 115, 108, 111, 97, 100, 101, 114, 47, 84, 101, 115,
            116, 72, 101, 108, 108, 111, 87, 111, 114, 108, 100, 1, 0, 16, 106, 97, 118, 97, 47, 108,
            97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 0, 33, 0, 3, 0, 4, 0, 0, 0, 0, 0, 2, 0, 1,
            0, 5, 0, 6, 0, 1, 0, 7, 0, 0, 0, 29, 0, 1, 0, 1, 0, 0, 0, 5, 42, -73, 0, 1, -79, 0, 0, 0,
            1, 0, 8, 0, 0, 0, 6, 0, 1, 0, 0, 0, 7, 0, 1, 0, 9, 0, 10, 0, 1, 0, 7, 0, 0, 0, 27, 0, 1,
            0, 1, 0, 0, 0, 3, 18, 2, -80, 0, 0, 0, 1, 0, 8, 0, 0, 0, 6, 0, 1, 0, 0, 0, 10, 0, 1, 0, 11,
            0, 0, 0, 2, 0, 12
    };

    @Override
    public Class<?> findClass(String name) throws ClassNotFoundException {
        // 只处理TestHelloWorld类
        if (name.equals(TEST_CLASS_NAME)) {
            // 调用JVM的native方法定义TestHelloWorld类
            return defineClass(TEST_CLASS_NAME, TEST_CLASS_BYTES, 0, TEST_CLASS_BYTES.length);
        }

        return super.findClass(name);
    }

    /**
     * 使用自定义类加载器加载TestHelloWorld类字节码并调用hello方法示例,等价于如下代码:
     * <p>
     *
     * </p>
     *
     * @param args
     */
    public static void main(String[] args) {
        // 创建自定义的类加载器
        TestClassLoader loader = new TestClassLoader();

        try {
            // 使用自定义的类加载器加载TestHelloWorld类
            Class testClass = loader.loadClass(TEST_CLASS_NAME);

            // 反射创建TestHelloWorld类,等价于 TestHelloWorld t = new TestHelloWorld();
            Object testInstance = testClass.newInstance();

            // 反射获取hello方法
            Method method = testInstance.getClass().getMethod("hello");

            // 反射调用hello方法,等价于 String str = t.hello();
            String str = (String) method.invoke(testInstance);

            System.out.println(str);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

 

 

首先继承ClassLoader 重写他的方法,这里定义了TEST_CLASS_NAME就是TestHelloWorld的包名到名字

 

 

 

看这个findClass,做一个判断,名字是否是我们所写的TestHelloWorld,然后return defineClass

是调用JVM的方法去定义TestHellowWorld类;

 

 

 

如果不是我们想创建的对象就 

return super.findClass(name);

用super调用父类的findclass去创建。

 

这里代码的main方法就是这样的流程,注意看注释

创建自定义的类加载器loader对象-->用我们重写的类加载器去加载TestHelloWorld类

-->然后通过反射该类,获取对应对象-->然后反射调用对象来invoke调用到hello方法

-->最后通过hello方法的返回str也就是hello world 输出

/**
     * 使用自定义类加载器加载TestHelloWorld类字节码并调用hello方法示例,等价于如下代码:
     * <p>
     *
     * </p>
     *
     * @param args
     */
    public static void main(String[] args) {
        // 创建自定义的类加载器
        TestClassLoader loader = new TestClassLoader();

        try {
            // 使用自定义的类加载器加载TestHelloWorld类
            Class testClass = loader.loadClass(TEST_CLASS_NAME);

            // 反射创建TestHelloWorld类,等价于 TestHelloWorld t = new TestHelloWorld();
            Object testInstance = testClass.newInstance();

            // 反射获取hello方法
            Method method = testInstance.getClass().getMethod("hello");

            // 反射调用hello方法,等价于 String str = t.hello();
            String str = (String) method.invoke(testInstance);

            System.out.println(str);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

 

ok来打断点走一遍整个调用流程首先是在加载我们的TestHelloWorld类


 

然后以为名字是if判断对应的名字,所以会进入到defineClass方法里面

 

 

 

 defineClass是会返回一个对象的


 

 

 我们用的是58行断点那里的testClass来接收这个返回值

这个返回值也就是TestHelloWorld的对象了,然后我们通过反射去调用这个对象

 

 

 

然后通过testInstance来反射获取到Hello方法

 

 

 最后就是通过反射调用hello方法来执行,返回给str 输出了hello world:

 

 

 

 

 



刚开始理解起来确实有些难,慢慢来把,学习之路,慢就是快~

标签:Java,--,TestHelloWorld,108,101,hello,97,加载
来源: https://www.cnblogs.com/byErichas/p/15629371.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有