Java Classloading Mechanism : ClassLoader & ASM & 动态字节码增强
ClassLoader装载一个类时:
自底向上检查是否已装载 - 过程中的每层:在本层cache中发现已装载,则返回这个已装载的类实例,调用结束;发现未加载,继续委托上层。
若直至最顶层都未发现该类的已装载实例:
自顶向下尝试装载 - 过程中的每层:经搜索本层path后装载类成功,则返回这个装载成功的类实例,调用结束;装载未成功,将ClassNotFoundException抛给下层,委托下层继续尝试装载。
若直至最底层都未装载成功:
则抛给应用一个ClassNotFoundException。
Java Class Loading:
http://www.techjava.de/topics/2008/01/java-class-loading/
The Java Tutorials -> Understanding Extension Class Loading:
http://download.oracle.com/javase/tutorial/ext/basics/load.html
Here are some highlights of the class-loading API:
Constructors in java.lang.ClassLoader and its subclasses allow you to specify a parent when you instantiate a new class loader. If you don't explicitly specify a parent, the virtual machine's system class loader will be assigned as the default parent.The loadClass method in ClassLoader performs these tasks, in order, when called to load a class: 1 If a class has already been loaded, it returns it.
2 Otherwise, it delegates the search for the new class to the parent class loader.
3 If the parent class loader does not find the class, loadClass calls the method findClass to find and load the class.The findClass method of ClassLoader searches for the class in the current class loader if the class wasn't found by the parent class loader. You will probably want to override this method when you instantiate a class loader subclass in your application.The class java.net.URLClassLoader serves as the basic class loader for extensions and other JAR files, overriding the findClass method of java.lang.ClassLoader to search one or more specified URLs for classes and resources.
Java Security Architecture -> 5 Secure Class Loading:
http://download.oracle.com/javase/1.4.2/docs/guide/security/spec/security-spec.doc5.html
深入探讨 Java 类加载器:
http://www.ibm.com/developerworks/cn/java/j-lo-classloader/index.html
每个 Java 类(补充:即每个Class实例对象)都维护着一个指向定义它的类加载器的引用,通过 getClassLoader() 方法就可以获取到此引用。
类加载器在尝试自己去查找某个类的字节代码并定义它时,会先代理(注:这里的代理指的是delegate,叫委托更好点)给其父类加载器,由父类加载器先去尝试加载这个类,依次类推。在介绍代理模式背后的动机之前,首先需要说明一下 Java 虚拟机是如何判定两个 Java 类是相同的。Java 虚拟机不仅要看类的全名是否相同,还要看加载此类的类加载器是否一样。只有两者都相同的情况,才认为两个类是相同的。即便是同样的字节代码,被不同的类加载器加载之后所得到的类,也是不同的。比如一个 Java 类 com.example.Sample,编译之后生成了字节代码文件 Sample.class。两个不同的类加载器 ClassLoaderA 和 ClassLoaderB 分别读取了这个 Sample.class 文件,并定义出两个 java.lang.Class 类的实例来表示这个类。这两个实例是不相同的。对于 Java 虚拟机来说,它们是不同的类。试图对这两个类的对象进行相互赋值,会抛出运行时异常 ClassCastException。
代理模式是为了保证 Java 核心库的类型安全。所有 Java 应用都至少需要引用 java.lang.Object 类,也就是说在运行的时候,java.lang.Object 这个类需要被加载到 Java 虚拟机中。如果这个加载过程由 Java 应用自己的类加载器来完成的话,很可能就存在多个版本的 java.lang.Object 类,而且这些类之间是不兼容的。通过代理模式,对于 Java 核心库的类的加载工作由引导类加载器来统一完成,保证了 Java 应用所使用的都是同一个版本的 Java 核心库的类,是互相兼容的。
不同的类加载器为相同名称的类创建了额外的名称空间。相同名称的类可以并存在 Java 虚拟机中,只需要用不同的类加载器来加载它们即可。不同类加载器加载的类之间是不兼容的,这就相当于在 Java 虚拟机内部创建了一个个相互隔离的 Java 类空间。
在前面介绍类加载器的代理模式的时候,提到过类加载器会首先代理给其它类加载器来尝试加载某个类。这就意味着真正完成类的加载工作的类加载器和启动这个加载过程的类加载器,有可能不是同一个。真正完成类的加载工作是通过调用 defineClass 来实现的;而启动类的加载过程是通过调用 loadClass 来实现的。前者称为一个类的定义加载器(defining loader),后者称为初始加载器(initiating loader)。在 Java 虚拟机判断两个类是否相同的时候,使用的是类的定义加载器。也就是说,哪个类加载器启动类的加载过程并不重要,重要的是最终定义这个类的加载器。两种类加载器的关联之处在于:一个类的定义加载器是它引用的其它类的初始加载器。如类 com.example.Outer 引用了类 com.example.Inner,则由类 com.example.Outer 的定义加载器负责启动类 com.example.Inner 的加载过程。
方法 loadClass() 抛出的是 java.lang.ClassNotFoundException 异常;方法 defineClass() 抛出的是 java.lang.NoClassDefFoundError 异常。
类加载器在成功加载某个类之后,会把得到的 java.lang.Class 类的实例缓存起来。下次再请求加载该类的时候,类加载器会直接使用缓存的类的实例,而不会尝试再次加载。也就是说,对于一个类加载器实例来说,相同全名的类只加载一次,即 loadClass 方法不会被重复调用。
ClassLoader的加载过程及分析一:
http://www.iteye.com/topic/703842引用虚拟机一启动,会先做一些初始化的动作。一旦初始化动作完成之后,就会产生第一个类别加载器,即所谓的Bootstrap Loader,Bootstrap Loader 是由C++ 所撰写而成,这个Bootstrap Loader所做的初始工作中,除了也做一些基本的初始化动作之外,最重要的就是加载定义在sun.misc 命名空间底下的Launcher.java 之中的ExtClassLoader( 因为是inner class ,所以编译之后会变成Launcher$ExtClassLoader.class) ,并设定其Parent 为null,代表其父加载器为Bootstrap Loader 。然后Bootstrap Loader ,再要求加载定义于sun.misc 命名空间底下的Launcher.java 之中的AppClassLoader( 因为是inner class,所以编译之后会变成Launcher$AppClassLoader.class) ,并设定其Parent 为之前产生的ExtClassLoader 实例。
AppClassLoader 和ExtClassLoader 都是URLClassLoader 的子类别。由于它们都是URLClassLoader 的子类别,所以它们也应该有URL 作为搜寻类别档的参考,由原始码中我们可以得知,AppClassLoader 所参考的URL 是从系统参java.class.path 取出的字符串所决定,而java.class.path 则是由我们在执行java.exe 时,利用 –cp 或-classpath 或CLASSPATH 环境变量所决定。
提醒我:关于JVM启动过程记得把附件中的 Using the BootClasspath.pdf 好好看看!
classloader相关基础知识:
http://www.iteye.com/topic/25053引用java动态载入class的两种方式:
1)implicit隐式,即利用实例化才载入的特性来动态载入class
2)explicit显式方式,又分两种方式:
1)java.lang.Class的forName()方法
2)java.lang.ClassLoader的loadClass()方法
深入了解Java ClassLoader、Bytecode 、ASM、cglib:
http://www.iteye.com/topic/98178
Java类加载原理解析第一篇(上):
http://weiwu83.iteye.com/blog/141207
Java类加载原理解析第一篇(下):
http://weiwu83.iteye.com/blog/141208
JDK引述:引用
Every Class object(每个Class类的实例对象) contains a reference to the ClassLoader that defined it.
protected Class<?> loadClass(String name,boolean resolve) throws ClassNotFoundException
Loads the class with the specified binary name. The default implementation of this method searches for classes in the following order:
1.Invoke findLoadedClass(String) to check if the class has already been loaded.
2. Invoke the loadClass method on the parent class loader. If the parent is null the class loader built-in to the virtual machine is used, instead.
3.Invoke the findClass(String) method to find the class.
If the class was found using the above steps, and the resolve flag is true, this method will then invoke the resolveClass(Class) method on the resulting Class object.
Subclasses of ClassLoader are encouraged to override findClass(String), rather than this method.