`
jian198001
  • 浏览: 1880 次
  • 性别: Icon_minigender_1
  • 来自: 北京
最近访客 更多访客>>
社区版块
存档分类
最新评论

JVM自定义类加载器--读取与JAVA类名不同的文件名--并解析加载类

阅读更多

本文的目的,是通过解剖和修改JVM的类加载器,来详细分析JVM的类加载机制。

其实任何一个JVM的类加载器不过是做了如下的工作:

1. 确定JAVA类文件的位置。

2. 读取类文件内容,将类文件内容读取成二进制字节流。

3. 解析并加载类内容。

4. 最后,将类的“标识”返回给要使用这个类的代码中。

那下面我们就来做一个比较“另类”的试验:

在JAVA规范中,public类名必须与类所在的文件名相同。

但本文将尝试在一个名为“T1.bin”的文件中,加载一个名为“Test1”的类,以此来创造一种与JVM标准类加载器不同的类加载器。


先讲讲我的实现思路:

1. 先编写一个类,此类将被其它类来调用,这个类的类名和文件名在测试时将不会相同。这个类的类名为“Test1”,而文件名为“T1.bin”。

2. 编写自己的类加载器,此类加载器不是从寻常的".class"文件中来加载一个类,而是从代码中写死的“D:/Users/T1.bin”文件中来加载一个类。

3. 编写业务逻辑类,此类将利用反射原理,调用类加载器,读取T1.bin文件,构造第一步中建立的类的对象,并调用这个对象的方法。

首先在Eclipse中新建一个Java工程,名为“Test1”,在我的硬盘上,此工程的存储地址为“D:\Users\haojian.XINAO\workspace\Test1”。

在此工程的src目录下,新建一个Java类,名为“Test1”,内容如下:

 

/**
 * @author haojian
 * 
 */
public class Test1 {

                // 此类的此方法,将被调用来测试
 public void testMethod() {

  System.out.print("---test---"); // 标识1

 }


}


则打开"D:\Users\haojian.XINAO\workspace\Test1"目录后,在src目录下将有Test1.java文件,在bin目录下将有“Test1.class”文件。


将Test1.class文件复制到D:\Users目录下,修改文件名为“T1.bin”。此时此T1.bin文件在逻辑上已与Eclipse中的Test1工程没有任何关系。


编写读取此T1.bin文件的类加载器:


在Test1目录下新建包,名为"cld",在此包下新建类,内容如下:

 

/**
 * 
 */
package cld;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.WritableByteChannel;

/**
 * @author haojian
 * 
 */
public class MyClassLoader1 extends ClassLoader {

 public MyClassLoader1(ClassLoader parent, String baseDir) {
  super(parent);
 }

 protected Class findClass(String name) throws ClassNotFoundException {
  byte[] bytes = loadClassBytes(name);
  Class theClass = defineClass(name, bytes, 0, bytes.length);
  if (theClass == null)
   throw new ClassFormatError();
  return theClass;
 }

 private byte[] loadClassBytes(String className)
   throws ClassNotFoundException {
  try {
   String classFile = getClassFile();
   FileInputStream fis = new FileInputStream(classFile);
   FileChannel fileC = fis.getChannel();
   ByteArrayOutputStream baos = new ByteArrayOutputStream();
   WritableByteChannel outC = Channels.newChannel(baos);
   ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
   while (true) {
    int i = fileC.read(buffer);
    if (i == 0 || i == -1) {
     break;
    }
    buffer.flip();
    outC.write(buffer);
    buffer.clear();
   }
   fis.close();
   return baos.toByteArray();
  } catch (IOException fnfe) {
   throw new ClassNotFoundException(className);
  }
 }

 private String getClassFile() {

  return "D:/Users/T1.bin";

 }

}


MyClassLoader1类实现了自定义的类加载器, getClassFile() 方法的作用,是确定要读取的文件名,loadClassBytes()方法则将文件中的内容读取成二进制数组。findClass()则可以看做是MyClassLoader1类与它的父类ClassLoader类之间的接口,后面将要讲到。

此类将从D:/Users/T1.bin文件中读取Java类内容,并加载到JVM中去。

下面将编写业务流程代码,去“T1.bin”文件中读取并加载“Test1”类。并调用Test1类的“testMethod”方法。

 

/**
 * 
 */
package cld;

import java.lang.reflect.Method;

/**
 * @author haojian
 * 
 */
public class TestClass {

 /**
  * @param args
  */
 public static void main(String[] args) {
  // TODO Auto-generated method stub

                                // 创建自己写的类加载器的对象
  MyClassLoader1 myClassLoader1 = new MyClassLoader1(TestClass.class.getClassLoader());

                                // 设定要加载的类名称
  String className = "Test1";

  Class loadedClassTest1 = null;

  try {
                                                // 实际干活,去“T1.bin”文件中加载"Test1"类
   loadedClassTest1 = myClassLoader1.loadClass(className); // 标识2
  } catch (ClassNotFoundException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }

                               // 下面是利用反射来实际调用Test1类的“testMethod”方法
  Object oTest1 = null;

  try {

   oTest1 = loadedClassTest1.newInstance();

   Method testMethodCall = null;

   testMethodCall = loadedClassTest1.getMethod("testMethod");

   testMethodCall.invoke(oTest1);

  } catch (Exception e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }

 }

}

 

以下为控制台输出结果:

---test---


可以看到,程序中确实实际调用了“标识1”位置的语句。


也许有人会问,MyClassLoader1这个类为什么会到D:/Users/T1.bin文件中去加载"Test1"类呢?在TestClass的main方法里,并没有调用getClassFile()方法啊?

让我们解析一下MyClassLoader1这个类的执行过程,大家就清楚了。。。


让我们先看标识2的代码,这里调用了MyClassLoader1这个类的loadClass(className)方法,这个方法做了什么工作呢?


我们查看MyClassLoader1类,发现没有loadClass方法,那么这个方法就应该是由它的父类来实现的。则我们继续打开ClassLoader这个类的loadClass方法:

    public Class<?> loadClass(String name) throws ClassNotFoundException {
 return loadClass(name, false);
    }

    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 = findBootstrapClass0(name);
  }
     } catch (ClassNotFoundException e) {
         // If still not found, then invoke findClass in order
         // to find the class.
         c = findClass(name);
     }
 }
 if (resolve) {
     resolveClass(c);
 }
 return c;
    }



此方法的执行过程如下:

1. 首先查看JVM中是否已经加载过这个类。


2. 如没有加载过这个类,则首先用此类的父类来加载参数类名中指向的类。


3. 如仍然不能加载类,则调用findClass方法。


因为我们已经确定这个类是新加载过的,以前没有加载,所以1、2两步必然不能正确加载此类,程序必然会执行到第3步。


则我们继续追踪findClass()此类的代码。。。

 

    protected Class<?> findClass(String name) throws ClassNotFoundException {
 throw new ClassNotFoundException(name);
    }


其实在ClassLoader类里,对findClass()是没有任何实现的,需要它的子类来实现。这样,执行权就顺利的交给了MyClassLoader1的findClass()方法,而通过findClass()->loadClassBytes()->getClassFile()就能顺利执行到getClassFile()方法了。

其实我这篇文章里面讲到的,都只是对类文件的读取,并没有涉及到最核心的“将二进制字节流解析成内存中的JAVA类结构”,将二进制解析成内存结构是由“defineClass()”方法来完成的,以后会慢慢讲到的。

其实按照我文章里的这种思路,大家可以尝试在数据库中、网站文件中、FTP中等地方来加载一个JAVA类。。。

1
0
分享到:
评论

相关推荐

    自定义类加载器实现自定义加载

    在Java编程语言中,类加载器(ClassLoader)是运行时环境...理解类加载机制并正确实现自定义加载器是提升Java应用开发能力的重要一步。在实际项目中,合理利用类加载器可以解决很多复杂问题,比如模块化、动态更新等。

    自定义类加载代码

    在Java编程语言中,类加载器(ClassLoader)是运行时环境的重要组成部分,它负责查找和加载类的字节码文件到Java虚拟机(JVM)中。本篇将深入探讨自定义类加载器的实现、双亲委派模型以及如何指定父类加载器。 首先...

    Java类加载原理解析

    自定义类加载器可以继承`java.lang.ClassLoader`来实现自己的加载逻辑,比如从网络、数据库或其他来源加载类。当需要加载非标准路径或实现特定加载策略时,自定义类加载器就显得尤为重要。 总之,Java类加载机制是...

    JVM类加载分析

    类加载器是JVM实现动态加载的核心组件,它的主要任务是根据类名找到对应的.class文件,并将其转换为内存中的Class对象。Java中的类加载器主要有以下几种: 1. Bootstrap ClassLoader:引导类加载器,负责加载JRE...

    Java类加载内幕

    1. **加载 (Loading):** 在这个阶段,类加载器读取类的二进制流并将其转换为 `java.lang.Class` 对象。 2. **连接 (Linking):** 包括验证 (Verification)、准备 (Preparation) 和解析 (Resolution) 三个子步骤。验证...

    JAVA动态加载JAR zip包

    在Java中,类加载器(ClassLoader)是负责加载类到JVM中的核心组件。默认情况下,系统类加载器会搜索`CLASSPATH`环境变量或 `-cp`/`-classpath`命令行选项指定的路径下的`.class`文件。但是,我们可以通过自定义类...

    01-JVM综合53问(带答案)

    - 在现有加载器不能满足需求时,例如加载非标准路径的类、加密/解密字节码或打破双亲委派模型,可自定义加载器。 12. **字节码对象的唯一性** - 同一类的不同字节码对象由不同的类加载器加载,因此它们在内存中是...

    Java类加载机制.doc

    加载阶段,Java虚拟机通过类加载器将类的.class文件读取到内存中,并创建对应的java.lang.Class对象。类加载器是这个过程的关键组件。 Java中主要有三种类加载器: 1. 引导类加载器(Bootstrap ClassLoader):这...

    CustomClassLoader:Java 自定义类加载器

    通过理解类加载原理和自定义加载器的工作方式,我们可以更灵活地控制类的生命周期,提高软件的可扩展性和安全性。在实际项目中,务必谨慎使用,遵循良好的设计原则,以确保代码的可维护性和稳定性。

    深入java虚拟机(七)深入源码看java类加载器ClassLoader 1

    《深入Java虚拟机(七)深入源码看java类加载器ClassLoader》 Java类加载器(ClassLoader)在Java运行环境中扮演着至关重要的角色。它负责将类的字节码加载到Java虚拟机(JVM)中,使得程序能够运行。ClassLoader是...

    Java ClassLoader Tutorial.zip

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

    loader in java houtian

    Java加载器(Loader)是Java虚拟机(JVM)的核心组成部分,主要负责将类的字节码文件加载到JVM中并转换为运行时的数据结构。在深入理解这个概念之前,我们首先要明白Java的类加载机制。Java的类加载过程包括加载、...

    Java虚拟机类装载机制

    Java虚拟机类装载机制是Java运行环境中的核心组成部分,它负责将类的字节码从磁盘、网络等不同来源加载到JVM中,并进行一系列处理以使类能够被正确地使用。类装载机制的目的是为了实现代码的动态加载和运行时的灵活...

    java之jvm学习笔记五(实践写自己的类装载器)

    类装载器在Java中的主要职责是动态加载类到JVM中。Java的类装载器分为三个基本层次:启动类装载器(Bootstrap ClassLoader)、扩展类装载器(Extension ClassLoader)和应用程序类装载器(Application ClassLoader)...

    揭示Java类加载内幕(code)

    1. **加载**:类加载器根据全限定名(包括包名和类名)找到对应的.class文件,然后读取字节流,并创建一个二进制表示的Class对象。 2. **验证**:确保加载的类符合Java语言规范,没有安全风险。这个阶段会检查字节...

    Java类加载

    本文详细介绍了 Java 类加载机制的基本概念,包括类加载的过程、初始化时机、类加载器的工作原理及其分类,并提供了一个自定义类加载器的示例。通过这些内容的学习,可以帮助开发者更好地理解 Java 类加载机制,为...

    Java类加载原理浅析

    当一个类加载器接收到加载类的请求时,它首先会委托父加载器去尝试加载,只有当父加载器无法完成时,子加载器才会尝试自己加载。这种模型避免了类的重复加载,也保证了核心类库的安全性。 **自定义类加载器**是Java...

    综合面试题.docx

    以上内容详细阐述了JVM中类的加载机制、类加载器及加载模型、内存区域划分、类的初始化时机以及运行时内存的具体划分等内容,这些知识点对于深入理解Java虚拟机的工作原理至关重要,也是Java开发人员面试中经常涉及...

    java classLoader 的全面讲解

    Java 类加载器(ClassLoader)是Java虚拟机(JVM)中的一个重要组成部分,它负责加载类的字节码文件,使得程序能够运行。深入理解ClassLoader对于优化应用性能、处理类加载问题以及实现自定义加载策略至关重要。 一...

Global site tag (gtag.js) - Google Analytics