Java如何自定义类加载器

类加载器的详细描述

我们可以编写自己的用于特殊目的的类加载器,这使得我们可以在向虚拟机传递字节码之前执行定制的检查。

如何自定义类加载器

如果想要编写自己的类加载器,只需要两步:

  • 继承ClassLoader类
  • 覆盖findClass(String className)方法

ClassLoader超类的loadClass方法用于将类的加载操作委托给其父类加载器去进行,只有当该类尚未加载并且父类加载器也无法加载该类时,才调用findClass方法。
如果要实现该方法,必须做到以下几点:

1.为来自本地文件系统或者其他来源的类加载其字节码。
2.调用ClassLoader超类的defineClass方法,向虚拟机提供字节码。

下面是自定义类加载器的一种实现方式:

public class CustomClassLoader extends ClassLoader {
    protected Class<?> findClass(String name) throws ClassNotFoundException {
      try {
          String cname = "/Users/wuzhenyu/Desktop/spring-boot/src/main/java/" + name.replace('.', '/') + ".class";
          byte[] classBytes = Files.readAllBytes(Paths.get(cname));
          Class<?> cl = defineClass(name, classBytes, 0, classBytes.length);
          if (cl == null) {
              throw new ClassNotFoundException(name);
          }
          return cl;
      } catch (IOException e) {
          System.out.print(e);
          throw new ClassNotFoundException(name);
      }
    }
}

我在该类的目录下准备了一个编译好的类文件SayHello.class(后缀改为了text),SayHello.class对应的Java代码如下:

public class SayHello {
   public static void main(String[] args) {
       System.out.print("Hello World");
   }
}

下面是测试类的代码:

测试前请删除SayHello.java文件

public class ClassLoaderTest {
    public static void main(String[] args) {
        try {
            ClassLoader loader = new CustomClassLoader();
            //调用loadClass加载sample.loader.SayHello类
            //无法加载到该类,因此会调用findClass方法
            Class<?> c = loader.loadClass("sample.loader.SayHello");
            Method m = c.getMethod("main", String[].class);
            m.invoke(null, (Object) new String[]{});
        } catch (Throwable e) {
            System.out.println(e);
        }
    }
}

运行结果如下:

Hello World

这是一个简单的实现自己类加载器的例子。在更复杂的案例中,使用的往往是加密过的类文件,加载该类字节码时,还需要解密。不然它们就不能由标准虚拟机来执行,也不能轻易被反汇编。

类加载器相关的API

1. java.lang.Class

  • ClassLoader getClassLoader(): 获取加载该类的类加载器

2.java.lang.ClassLoader

  • ClassLoader getParent():返回父类加载器,如果父类加载器是引导类加载器,则返回null。

  • static ClassLoader getSystemClassLoader():获取系统类加载器,即用于加载第一个应用类的类加载器。

  • protected Class findClass(String name):类加载器应该覆盖该方法,以查找类的字节码,并通过调用defineClass方法将字节码传给虚拟机。在类的名字中,使用.作为包名分隔符,并且不使用.class后缀。

  • Class definedClass(String name, byte[] byteCodeData, int offset, int length):将一个新的类添加到虚拟机,其字节码在给定的数据范围中。

3.java.net.URLClassLoader

  • URLClassLoader(URL[] urls)
  • URLClassLoader(URL[] urls, ClassLoader parent) :构建一个类加载器,它可以从给定的URL处加载类。如果URL以 / 结尾,那么它表示的一个目录,否则,它表示的是一个JAR文件。

4.java.lang.Thread

  • ClassLoader getContextClassLoader():获取类加载器,该线程的创建者将其指定为执行该线程时最适合使用的类加载器。

  • void setContextClassLoader(ClassLoader loader):为该线程中的代码设置一个类加载器,以获取要加载的类。如果在启动一个线程时没有显式地设置上下文类加载器,则使用父线程的上下文类加载器。

参考文献《Java 核心技术 卷二》

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

友情链接更多精彩内容