美文网首页程序员Java 杂谈
Java如何自定义类加载器

Java如何自定义类加载器

作者: 打铁大师 | 来源:发表于2018-09-14 23:21 被阅读0次

    类加载器的详细描述

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

    如何自定义类加载器

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

    • 继承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 核心技术 卷二》

    相关文章

      网友评论

        本文标题:Java如何自定义类加载器

        本文链接:https://www.haomeiwen.com/subject/orvagftx.html