`
orange5458
  • 浏览: 347788 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

关于ClassLoader

阅读更多

1.简介

ClassLoader,顾名思义就是类加载器,负责将Class加载到JVM中,将Class字节码重新解析成JVM统一要求的对象格式,除此之外它还负责审查每个类应该由谁加载,它是一种父优先的等级加载机制。

2.ClassLoader类结构分析

1)protect ... defineClass(...)

将一个字节数组转换为 Class 类的实例。

注:如果直接调用这个方法生成类的Class对象,这个类的Class对象还没有resolve,这个resolve将会在这个对象真正实例化时才进行。

2)protect ... findClass(...)

使用指定的二进制名称查找类。默认实现抛出一个ClassNotFoundException,我们通过覆盖它来实现类的加载规则,从而取得要加载类的字节码,然后调用defineClass方法生成类的Class对象,如果你想在类被加载到JVM中时就被连接,那么可以接着调用resolveClass方法。ClassLoader按照委托模型来加载类,在通过父类加载器检查所请求的类失败后,此方法才会被loadClass方法所调用。

3)loadClass

使用指定的二进制名称来加载类。此方法的默认实现将按以下顺序搜索类:

 (1)调用 findLoadedClass(String) 来检查是否已经加载类。

 (2)在父类加载器上调用 loadClass 方法。如果父类加载器为 null,则使用虚拟机的内置类加载器。

 (3)调用 findClass(String) 方法查找类。

 (4)如果使用上述步骤找到类,并且 resolve 标志为真,则此方法将在得到的 Class 对象上调用 resolveClass(Class) 方法。

 鼓励用 ClassLoader 的子类重写 findClass(...),而不是此方法。

4)resolveClass

 链接指定的类。

3.ClassLoader的等级加载机制

就是指ClassLoader.loadClass方法的步骤,上文已经描述过,源代码如下:

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

protected synchronized Class<?> loadClass(String paramString, boolean paramBoolean)
  	throws ClassNotFoundException {
  Class localClass = findLoadedClass(paramString);
  if (localClass == null) {
    try {
      if (this.parent != null) {
        localClass = this.parent.loadClass(paramString, false);
      } else {
        localClass = findBootstrapClass0(paramString);
      }
    } catch (ClassNotFoundException localClassNotFoundException) {
      localClass = findClass(paramString);
    }
  }
  if (paramBoolean) {
    resolveClass(localClass);
  }
  return localClass;
}

 好处:

1)防止重复加载。

2)考虑到安全因素,保证所有的类都由正确的ClassLoader加载。

4.ClassLoader的层级结构

1)Bootstrap ClassLoader

主要加载JVM自身工作需要的类,完全由JVM控制,需要加载哪个类,如何加载都是由JVM自己控制,别人也访问不到这个类,所以这个ClassLoader不遵守前面介绍的加载规则,它仅仅是一个加载工具而已,既没有更高一级的父加载器,也没有子加载器。

2)ExtClassLoader

服务的特定目标是在System.getProperty("java.ext.dirs")目录下,由Bootstrap ClassLoader加载。

3)AppClassLoader

parent是ExtClassLoader,服务的特定目标在System.getProperty("java.class.path")目录下,也就是我们常用的classpath,由Bootstrap ClassLoader加载。

4)自定义ClassLoader

假如不指定,则默认的parent为AppClassLoader,因为默认的parent = getSystemClassLoader(),而该方法返回的正是AppClassLoader。

protected ClassLoader()
{
  SecurityManager localSecurityManager = System.getSecurityManager();
  if (localSecurityManager != null) {
    localSecurityManager.checkCreateClassLoader();
  }
  this.parent = getSystemClassLoader();
  this.initialized = true;
}

 测试:ClassLoaderTest.java

package com.siyuan.test.lang;

import java.net.URL;
import java.net.URLClassLoader;

import sun.util.resources.CalendarData_zh;

public class ClassLoaderTest {

	/**
	 * @param args
	 * @throws Exception 
	 */
	public static void main(String[] args) throws Exception {
		//ExtClassLoader的服务路径
		System.out.println(System.getProperty("java.ext.dirs"));
		//AppClassLoader的服务路径
		System.out.println(System.getProperty("java.class.path"));
		//String由Bootstrap ClassLoader加载
		System.out.println("ClassLoader of String : " + String.class.getClassLoader());
		//CalendarData_zh由ExtClassLoader加载
		System.out.println(CalendarData_zh.class.getClassLoader());
		//ClassLoaderTest由AppClassLoader加载
		ClassLoader classLoader =  ClassLoaderTest.class.getClassLoader();
		System.out.println("ClassLoader of ClassLoaderTest : " + classLoader);
		//StringTest由URLClassLoader加载
		ClassLoader diyClassLoader = new URLClassLoader(new URL[]{new URL("file://D:/testworkspace/jdktest/testbin/")});
		System.out.println("ClassLoader of StringTest : " + diyClassLoader.loadClass("com.siyuan.test.filter.StringTest").getClassLoader());
		//ClassLoader层级结构
		classLoader = diyClassLoader;
		do {
			System.out.println("ClassLoader info --------------");
			System.out.println("name : " + classLoader);
			System.out.println("parent : " + classLoader.getParent());
			System.out.println("class path : " + classLoader.getResource(""));
			System.out.println("ClassLoader of it : " + classLoader.getClass().getClassLoader());
			classLoader = classLoader.getParent();
		} while(classLoader != null);
	}

}

 运行结果:

C:\Program Files\Java\jdk1.6.0_10\jre\lib\ext;C:\Windows\Sun\Java\lib\ext
D:\testworkspace\jdktest\bin
ClassLoader of String : null
sun.misc.Launcher$ExtClassLoader@addbf1
ClassLoader of ClassLoaderTest : sun.misc.Launcher$AppClassLoader@19821f
ClassLoader of StringTest : java.net.URLClassLoader@61de33
ClassLoader info --------------
name : java.net.URLClassLoader@61de33
parent : sun.misc.Launcher$AppClassLoader@19821f
class path : file:/D:/testworkspace/jdktest/bin/
ClassLoader of it : null
ClassLoader info --------------
name : sun.misc.Launcher$AppClassLoader@19821f
parent : sun.misc.Launcher$ExtClassLoader@addbf1
class path : file:/D:/testworkspace/jdktest/bin/
ClassLoader of it : null
ClassLoader info --------------
name : sun.misc.Launcher$ExtClassLoader@addbf1
parent : null
class path : null
ClassLoader of it : null

5.加载方式

1)隐式加载:不通过在代码里调用ClassLoader来加载需要的类,而是通过JVM来自动加载需要的类。例如,当我们在类中继承或者引用某个类时,JVM在解析当前这个类时发现引用的类不在内存中,那么就会自动将这些类加载到内存中。

2)显式加载:在代码中通过调用ClassLoader来加载类,如this.getClassLoader().loadClass(xxx)或者Class.forName(xxx)。

6.class文件加载过程

 

1)找到.class文件并把这个文件包含的字节码加载到内存中

2.1)字节码验证,确保格式正确,行为正确

2.2)类准备,包含每个类中定义的字段,方法和实现接口的结构分析和内存分配。

2.3)解析,装入类所引用的其他所有类,如超类,接口,字段,方法签名,方法中使用的变量。

3)类中静态属性和初始化赋值,以及静态块的执行

7.如何判断两个class是否相同

1)它们的完整类名是否一致

2)加载它们的ClassLoader是否是同一个

测试:ClassLoaderTest.java

package com.siyuan.test.lang;

import java.net.URL;
import java.net.URLClassLoader;

import sun.util.resources.CalendarData_zh;

public class ClassLoaderTest {

	/**
	 * @param args
	 * @throws Exception 
	 */
	public static void main(String[] args) throws Exception {
		ClassLoader diyClassLoader1 = new URLClassLoader(new URL[]{new URL("file://D:/testworkspace/jdktest/testbin/")});
		Class stringTestClass1 = diyClassLoader1.loadClass("com.siyuan.test.filter.StringTest");
		ClassLoader diyClassLoader2 = new URLClassLoader(new URL[]{new URL("file://D:/testworkspace/jdktest/testbin/")});
		Class stringTestClass2 = diyClassLoader2.loadClass("com.siyuan.test.filter.StringTest");
		System.out.println(stringTestClass1 == stringTestClass2);
	}

}

 运行结果

false

 8.参考资料

《深入分析 Java Web 技术内幕》

  • 大小: 25.1 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics