java1.1中的实现
java1.1的类加载机制相对单一,而用户自定义加载器的重写却比较复杂。。
主要需要重写ClassLoader中一个方法:Class loadClass(String name)。
Class loadClass(String name):loadClass(String name)(或者loadClass(String name , boolean resolve))这个方法是加载的核心,他将根据类名(全名,比如java.lang.String)获得对应类的二进制数据,然后通过Class defineClass(byte[] b)将二进制数据加载到JVM的方法区,defineClass返回对应类的Class 实例,并根据可选的参数resolve决定是否需要现在解析这个类,之后将这个Class实例作为 loadClass方法的返回值。
其中,若是没有办法实现加载和defineClass,即无法通过本加载器直接加载类的情况,则使用Class findSystemClass(String name)委派系统的加载器查找,如果能找到则加载,否则,抛出ClassNotFoundException。可见,这个方法必须放在 loadClass方法中自定义加载的代码之后。
重写loadClass方法还是相对复杂的,需要考虑到各种加载情况。
以下的例子实现了这一过程。
它是一个能直接由.java源文件实现类加载的 加载器:CompilingClassLoader。
就像之前说到的,只需要重写loadClass()方法,用户自定义的加载实现逻辑都在这个方法中。至于代码中的其中的
其他的方法,比如defineClass(String name),findSystemClass(String name)都是由loadClass调用的。
CompilingClassLoader类中还包含了执行对去字节码、编译java文件的方法,都为loadClass()方法所用。
在defineClass()或findSystemClass()执行结束前,可能返回的异常挺多的:NoClassDefFoundError ,ClassNotFoundError , ClassFormatError , UnsupportedClassVersionError , LinkageError , ClassCircularityError , IncompatibleClassChangeError . 详细信息请大家查阅java doc。
代码如下:
CompilingClassLoader.java
- <span style="font-size: 16px; ">import java.lang.reflect.*;
-
-
-
-
-
-
-
-
-
public class CCLRun
- {
-
static public void main( String args[] ) throws Exception {
-
-
-
-
String progClass = args[0];
-
-
-
-
-
String progArgs[] = new String[args.length-1];
-
System.arraycopy( args, 1, progArgs, 0, progArgs.length );
-
-
-
CompilingClassLoader ccl = new CompilingClassLoader();
-
-
- Class clas = ccl.loadClass( progClass );
-
-
-
-
-
-
Class mainArgType[] = { (new String[0]).getClass() };
-
-
-
Method main = clas.getMethod( "main", mainArgType );
-
-
-
- Object argsArray[] = { progArgs };
-
-
-
main.invoke( null, argsArray );
- }
- }
Foo.java
<span style="font-size: 16px; ">public class Foo
- {
-
static public void main( String args[] ) throws Exception {
-
System.out.println( "foo! "+args[0]+" "+args[1] );
- }
- }
- java CCLRun Foo arg1 arg2
- CCL: Compiling Foo.java...
- foo! arg1 arg2
具体参见资料
这是一篇2001年的文章,是早年java1.1的实现方式。如今的java已经改变了 很多,变得更加人性化,多功能化,鲁棒性也更强了。
java1.2以后的实现
具体参见资料改版以后,ClassLoader实现了parent-child模型,更好的控制安全性方面的问题。
为了延续parent-first的模式,通常在继承ClassLoader时不用重写loadClass()方法,而是重写findClass()方法。
findClass() : 方法中需要定义如何获取类的字节码,并使用defineClass()完成加载(仅仅加载,没有resolve等步骤)。
对应的有一个findLoadedClass() ,这个方法用来实现对查找当前加载器是否有加载某类。由于findClass是从原来的loadClass方法中抽离的代码,重写也简单很多,省去了开发者对流程控制的很多顾虑。
loadClass():如果使用parent-first的加载模型,loadClass()方法是不用重写的。它通过向父亲加载器迭代实现了parent-first的委托关系。每次加载一个类时,先调findLoadedClass(),如果没有找到,则调用父亲加载器的loadClass(),如果找到了就返回Class实例,没有找到则父亲加载器会产生一个ClassNotFoundException,捕捉到这个Exception后,加载器会自己调用findClass()尝试实现对类的加载。如果依然没有成功加载,则产生一个ClassNotFoundException.
A.loadClass()
- |
- (not-found?) (by findLoadedClass)
- |
- B.loadClass()
- |
- (not found?) (by findLoadedClass)
- |
- systemclassloader.loadClass (Bs parent, also can be
- | called classpath classloader)
- |
- (not found?) (by findLoadedClass)
- |
- ootstrap classloader.loadClass (the bootstrap classloader,
- | (this has no parent)
- |
- (not found?)
- |
- systemclassloader.findClass (on system classloader,
- | will try to "find" class in "classpath")
- |
- (not found?) ClassNotFoundException
- |
- B.findClass
- |
- (not found?) ClassNotFoundException
- |
- A.findClass
- |
- (not found?)
- |
- ClassNotFoundException
注意,对于extensions class loader ,它的parent加载器是null,因为bootstrap加载器是本地实现的,并非java实现,于是,如何从extension 加载器向上回溯呢?答案如下:
try {
-
if (parent != null) {
-
c = parent.loadClass(name, false);
-
} else {
- c = findBootstrapClassOrNull(name);
- }
-
} catch (ClassNotFoundException e) {
-
-
- }
这是
ClassLoader的源代码,对于 parent 为null的情况,会直接调用findBootstrapClassOrNull方法尝试用bootstrap加载器加载 。通过源代码,能够很好的理解这里的parent-child 模型了。
另注意 对于基于parent-child模型的类加载器实现,都需要定义一个以parent类加载器作为参数的构造函数,以指定父加载器。如果直接调用没有参数的构造函数,则默认制定的是systemclassloader作为parent。
以下为编程实例
下面的例子是我用来实现动态分析java类关系的加载器代码。具体方法是:调用ASM api , 在加载器中加载类时,修改.class文件中的字节码,加入相应语句,让对象在创建或执行相应指令时,在trace文件中记录自己的行为。不过实现的方式不是重点,重点是使用了自定义的类加载器的实现!
在编码的过程中,我遇到的一个错误是,将需要使用自定义加载器加载的类文件直接放在了eclipse工程中的bin目录下。而这个目录是可以通过appclassloader即systemclassloader找到路径并加载的。根据parent-first的实现,这些类直接被systemclassloader加载了,也就绕过了自定义加载器的处理机制。修改过路径以后没有出现相应问题了。
ASMClassLoader.java
package biaobiaoqi.classLoader;
-
import java.io.*;
-
-
import org.objectweb.asm.ClassReader;
-
import org.objectweb.asm.ClassWriter;
-
-
import biaobiaoqi.asm.*;
-
-
-
public class ASMClassLoader extends ClassLoader
- {
- String basePath ;
-
-
-
-
-
public ASMClassLoader(String path){
- basePath = path ;
- }
-
-
-
-
-
-
-
public ASMClassLoader(String path , ClassLoader parent){
-
super(parent);
- basePath = path ;
- }
-
-
@Override
-
public Class findClass(String name) throws ClassNotFoundException{
-
System.out.println("findClass");
-
byte[] raw;
-
try {
- raw = getBytesFromBasePath( name );
-
} catch (IOException e) {
- e.printStackTrace();
-
throw new ClassNotFoundException();
- }
-
-
byte[] transformed = instrumentBtyeCode(raw);
-
-
-
-
-
-
-
-
-
-
-
if (transformed == null){
-
throw new ClassNotFoundException();
- }
-
-
-
return defineClass(name, transformed, 0, transformed.length );
- }
-
-
private byte[] getBytesFromBasePath( String className ) throws IOException ,ClassNotFoundException{
-
String fileStub = className.replace( '.', '/' );
-
String classFileName = basePath +fileStub+".class";
-
File file = new File( classFileName );
-
-
long len = file.length();
-
byte raw[] = new byte[(int)len];
-
FileInputStream fin = new FileInputStream( file );
-
-
int r = fin.read( raw );
-
if (r != len)
-
throw new IOException( "Can't read all, "+r+" != "+len );
-
- fin.close();
-
return raw;
- }
-
-
private byte[] instrumentBtyeCode(byte[] raw){
-
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
-
ASMClassAdapter mca = new ASMClassAdapter(cw);
-
ClassReader cr = new ClassReader(raw);
-
cr.accept(mca, 0);
-
return cw.toByteArray();
- }
-
-
@Override
-
public Class loadClass( String name, boolean resolve )
-
throws ClassNotFoundException {
-
System.out.println("loadClass_resolve");
-
return super.loadClass(name ,resolve);
- }
-
-
-
@Override
-
public Class loadClass( String name )
-
throws ClassNotFoundException {
-
System.out.println("loadClass");
-
return super.loadClass(name );
- }
-
- }
温馨提示:不要把需要用自定义加载器加载的类文件直接放在system classloader能达到的路径下,否则,parent-first会帮你加载好那个类。特别是在用eclipse等IDE开发的时候。。这个bug让我花了很长时间。。
分享到:
相关推荐
在Java编程语言中,ClassLoader是一个至关重要的组成部分,它负责加载类到JVM(Java虚拟机)中。理解ClassLoader的工作原理以及如何定制它,对于深入学习Java的运行机制和进行高级应用开发具有重要意义。本篇文章将...
在Java编程语言中,ClassLoader是核心组件之一,它负责加载类到JVM(Java虚拟机)中。自定义ClassLoader允许开发者根据特定需求加载类,比如动态加载或更新类文件,这在某些高级应用场景中非常有用,如插件系统、热...
在Java编程语言中,ClassLoader是一个至关重要的组成部分,它负责加载类到JVM(Java虚拟机)中执行。这篇测试主要探讨了ClassLoader的...通过测试和实践,我们可以更好地掌握ClassLoader的使用,提升Java编程的技能。
在Java编程语言中,ClassLoader是核心组件之一,它负责加载类到JVM(Java虚拟机)中,使得程序能够执行。ClassLoader原理的理解对于深入学习Java和进行系统优化至关重要。这篇博文将带你深入了解ClassLoader的工作...
在Java编程环境中,ClassLoader是一个至关重要的组件,它负责加载类到Java虚拟机(JVM)中。当我们需要从外部jar包动态加载类时,自定义ClassLoader就显得尤为关键。这篇博文"定义ClassLoader调用外部jar包"探讨了...
在Java编程语言中,类加载器(ClassLoader)是运行时环境的核心组成部分,它负责将类的字节码从各种来源(如JAR文件、网络、内存等)加载到Java虚拟机(JVM)中,使得程序能够运行。本文将深入探讨“使用classloader...
基于Java的多样化编程实践 该项目是一个用于练习和展示Java各种库和框架的实践项目,使用Maven构建。涵盖了从消息服务、切面编程、AI平台SDK到设计模式、Spring框架等多个方面的实现。 主要功能模块 消息服务 ...
在Java编程语言中,`ClassLoader`是一个至关重要的组件,它负责加载类到JVM(Java虚拟机)中。深入理解`ClassLoader`的源码对于Java开发者来说是提升技能的重要环节,尤其是在进行模块化、插件化开发或者进行系统...
#### 三、自定义ClassLoader实践 为了更深入地理解ClassLoader的工作机制,我们将构建一个简单的自定义ClassLoader示例,该ClassLoader能够在加载之前自动编译代码。 **1. 自定义ClassLoader的基本步骤** - **...
在Java编程语言中,ClassLoader是核心组件之一,它负责加载类到JVM(Java虚拟机)中执行。这篇博文深入探讨了ClassLoader的工作原理及其在实际应用中的重要性。以下是ClassLoader的相关知识点: 1. 类加载机制: ...
《XWiki Commons Classloader Protocol Jar 5.4与LaZyWorker开源项目...这两个项目都体现了开源社区的创新精神和对软件性能优化的执着追求,对于学习和实践Java编程、类加载机制以及优化策略的开发者具有极高的价值。
Java虚拟机(JVM)是Java程序运行的基础,它通过加载、验证、解析和执行字节码来实现程序的运行。在JVM中,类加载器(ClassLoader)扮演着至关重要的...通过实践和学习,我们能够提升Java编程的技能和解决问题的能力。
在Java编程语言中,类加载器(ClassLoader)是一个至关重要的组件,它负责将类的字节码加载到Java虚拟机(JVM)中,从而使程序能够运行。在这个简单的例子中,我们将探讨类加载器的基本概念、工作原理以及如何自定义...
在Java编程语言中,ClassLoader是核心的组成部分,它负责加载类到JVM(Java虚拟机)中。`ClassLoaderDemo.7z`这个压缩包提供的示例,旨在帮助我们理解ClassLoader的工作原理,特别是双亲委托模型以及如何打破这个...
- 在方法体中插入代码,实现AOP(面向切面编程)的动态代理。 ASM的使用通常涉及以下几个步骤: 1. 创建ClassWriter对象,它是字节码生成器,用于构建新的类或修改已有类。 2. 使用Visit接口访问类的结构,如类名、...
在实习期间,我尝试编写了自己的类加载器,这不仅加深了我对ClassLoader工作原理的理解,也锻炼了我的编程实践能力。这个过程让我认识到,作为一名JAVA工程师,不仅要掌握基本的语法和框架,还要理解JVM的底层运作,...
总的来说,这个“classloader-example”不仅是一个学习Java类加载器工作原理的好资源,也是一个实践自定义加载策略和运行时动态加载的实战案例。通过深入研究这个示例,开发者可以更好地掌握Java的高级特性,提高...
在Java编程中,理解和掌握一些...在学习Java的过程中,实践是至关重要的,通过在命令行环境中编译和运行程序,可以加深对这些概念的理解。不断探索和实验,将有助于你在Java编程领域积累丰富的经验,避免不必要的弯路。
Java编程经验分享主要涵盖三个关键知识点:动态加载机制、类文件查找原理以及JDK与JRE的区别。 1. 动态加载机制: Java的动态加载机制是其面向对象特性的一个...通过实践和探索,可以更深刻地领会Java的魅力和灵活性。
### JAVA编程经验汇总 在Java开发领域,积累丰富的实践经验对于提升个人技能至关重要。本文将根据提供的文件信息,总结并深入探讨几个重要的Java编程知识点。 #### 1. Java与C++面向对象思想的区别 学习Java时,...