`
lilisalo
  • 浏览: 1112210 次
文章分类
社区版块
存档分类
最新评论

ClassLoader编程实践

 
阅读更多

java 的类加载机制背景知识:

Java 类加载器( ClassLoader)浅析

Three principles of Classloader operation(Classloader操作三原则)

以上博文中所提及的java类加载机制,都是java1.2及以后的版本,而在最早的java1.1中,是没有parent-child模式的。

这里,将分别对java1.1 和 java1.2及以后的 类加载版本进行展示。



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
CCLRun.java

Foo.java

执行如下:
具体参见资料

来自IBM DeveloperWorks :Understanding the Java ClassLoader

这是一篇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.

控制流程如图:这里显示了一个类未能成功加载所要经历的流程。(来自参考资料http://stackoverflow.com/questions/3544614/how-is-the-control-flow-to-findclass-of



注意,对于extensions class loader ,它的parent加载器是null,因为bootstrap加载器是本地实现的,并非java实现,于是,如何从extension 加载器向上回溯呢?答案如下:
这是 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

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics