Java Classloader 类加载篇(1)

目录
一、ClassLoader 类加载器介绍
二、ClassLoader 类加载器的工作机制分析
三、WebAppClassLoader 模拟实现一个简单的Web应用类加载器

[b][color=blue]一、ClassLoader 类加载器介绍[/color][/b]
这里使用的JDK是sun的jdk1.6.0_18版本,重在阐述原理和具体版本没有多大的关系。

JVM 中预定义了3种类型的类加载器:启动类加载器、扩展类加载器、系统类加载器(也叫应用类加载器)。当一个 JVM 虚拟机启动后,它默认使用这3种。

1. 启动类加载器(bootstrap)是虚拟机实现的一部分,它负责将sun.boot.class.path路径中的Java API类加载到内存中。由于启动类加载器是由虚拟机的本地代码实现,因而开发者无法在Java程序中直接获取启动类加载器的引用。

2. 扩展类加载器(extension)是由sun.misc.Launcher$ExtClassLoader实现的,它负责将java.ext.dirs路径中的Java类加载到内存中,在Java程序中可以直接引用。

3. 系统类加载器(system)是由sun.misc.Launcher$AppClassLoader实现的,它负责将java.class.path路径中的Java类加载到内存中,在Java程序中可以直接引用。

[b][color=blue]二、ClassLoader 类加载器的工作机制分析[/color][/b]
JVM 中默认采用父子关系的类加载机制,通过启动类加载器、扩展类加载器、系统类加载器依次从上而下形成父子关系。
[quote="引用JDK API文档"]
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.
[/quote]
JVM“扩展类加载器”与“系统类加载器”类型层次图
[img]http://dl.iteye.com/upload/attachment/245449/ce6da46c-8327-3486-b22a-55d9ba5855f9.jpg[/img]
[img]http://dl.iteye.com/upload/attachment/245451/aca7693a-614f-3b04-839a-48a383bf4857.jpg[/img]
通过上面两图可以看出sun.misc.Launcher$ExtClassLoader 与 sun.misc.Launcher$AppClassLoader 均继承 java.lang.ClassLoader 抽象类

阅读java.net.URLClassLoader、java.security.SecureClassLoader、sun.misc.Launcher$ExtClassLoader、sun.misc.Launcher$AppClassLoader 的源码,可以看出它们都没有覆写 java.lang.ClassLoader 中的默认加载规则 loadClass(String,boolean) 方法,这样就可以通过 loadClass 方法的代码分析出,虚拟机默认采用的父子加载机制到底是咋样的。

父子加载机制说明:假设在某个特定的类加载器接到类加载请求时

protected synchronized Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
//首先查找cache中是否已有对应的Class
Class c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
//如果没有再委托给它的父类加载器,依次递归
c = parent.loadClass(name, false);

} else {
//父类加载器不存在时使用“启动类加载器”
c = findBootstrapClass0(name);
}
} catch (ClassNotFoundException e) {
//只有父类或“启动类加载器”都无法完成加载时,才自己去加载。
c = findClass(name);
}
}
if (resolve) {
//连接处理,为类变量分配内存初始化默认值、解析符号引用
resolveClass(c);
}
return c;
}



[b][color=blue]三、WebAppClassLoader 模拟实现一个简单的Web应用类加载器[/color][/b]
这个简单的模拟需要在eclipse工作空间中建立2个项目,一个java项目,一个web项目

Java项目:就包含一个WebAppClassLoader.java

package test.classloader;
import java.io.File;
import java.io.FilenameFilter;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
/**
* 模拟实现一个简单的Web应用类加载器
* @author linliuwei
* @create 2010-5-7
*/
public class WebAppClassLoader {
private URLClassLoader myClassLoader = null;

/**
* 如: new WebAppClassLoader("D:/workspace/webapp/WebRoot")
* @param webroot web应用的根目录
*/
public WebAppClassLoader(String webroot) {
URL[] urls = null;
try {
urls = getJARURLs(webroot);
} catch (MalformedURLException e) {
System.out.println(e.getMessage());
}
myClassLoader = new URLClassLoader(urls);
}

public Class<?> loadClass(String className) throws ClassNotFoundException {
return myClassLoader.loadClass(className);
}

/**
* 获得web应用下的所有classpath的URL
*
* <pre>
* WEB-INF\classes 目录
* WEB-INF\lib 下的所有jar文件
* </pre>
* @param webroot
* @return
* @throws MalformedURLException
*/
private URL[] getJARURLs(String webroot) throws MalformedURLException {
if (!webroot.endsWith(File.separator)) {
webroot += File.separator;
}

// web应用中的WEB-INF\classes
File classesDir = new File(webroot + File.separator + "WEB-INF" + File.separator + "classes");

// web应用中的WEB-INF\lib
File libDir = new File(webroot + File.separator + "WEB-INF" + File.separator + "lib");

// 找出WEB-INF\lib目录下所有的jar文件
File[] jarFiles = null;
if (libDir.isDirectory()) {
// 文件名过滤器,只允许.jar文件
jarFiles = libDir.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
// jar文件所在目录必须是一个文件夹
if (dir.isDirectory() && name.endsWith(".jar")) {
return true;
}
return false;
}
});
}
int jarCount = null == jarFiles ? 0 : jarFiles.length;
URL[] urls = new URL[1 + jarCount];
urls[0] = classesDir.toURI().toURL();// WEB-INF\classes
for (int i = 0; i < jarCount; i++) { // WEB-INF\lib下的所有jar文件
urls[i + 1] = jarFiles[i].toURI().toURL();
}
return urls;
}

private static void printClassLoader(ClassLoader classLoader) {

if (classLoader != null) {
System.out.println("\tClassLoader: " + classLoader);
if (classLoader.getParent() != null) {
printClassLoader(classLoader.getParent());
}
} else {
System.out.println("\t此类的类加载器为“启动类加载器”,它是由虚拟机的本地代码实现,在Java程序中无法引用");
}
}

// 打印类加载器的父子层次结构
public static void printClassLoaderHierarchy(Class<?> clazz) {
System.out.println(clazz.getName());
if (clazz.getClassLoader() != null) {
System.out.println("\t类加载器层次结构依次由子到父");
}
printClassLoader(clazz.getClassLoader());
System.out.println();
}

public static void printClassPath() {
String separator = System.getProperty("path.separator");

System.out.println("------------- bootstrap: sun.boot.class.path -------------");
System.out.println(System.getProperty("sun.boot.class.path").replaceAll(separator, "\n"));
System.out.println();

System.out.println("------------- extension: java.ext.dirs -------------");
System.out.println(System.getProperty("java.ext.dirs").replaceAll(separator, "\n"));
System.out.println();

System.out.println("------------- system: java.class.path -------------");
System.out.println(System.getProperty("java.class.path").replaceAll(separator, "\n"));
System.out.println();
}

public static void main(String[] mainArgs) {
try {
printClassPath();

printClassLoaderHierarchy(sun.misc.Launcher.class);
printClassLoaderHierarchy(String.class);
printClassLoaderHierarchy(Integer.class);
printClassLoaderHierarchy(int.class);
printClassLoaderHierarchy(ArrayList.class);

// 创建web应用类加载器
WebAppClassLoader classLoader = new WebAppClassLoader("D:/test-work/webapp/WebRoot");
printClassLoaderHierarchy(classLoader.getClass());

Class<?> mockServlet = classLoader.loadClass("test.classloader.MockServlet");
printClassLoaderHierarchy(mockServlet);

// 用反射调用main方法
String[] args = new String[] {};
mockServlet.getMethod("main", args.getClass()).invoke(null, (Object) args);

System.out.println("测试结束");
} catch (Exception e) {
e.printStackTrace();
}
}
}


web项目:包含MockResponse.java、MockServlet.java、test.jar(这个jar包就包含一个WebAppClassLoader.java文件)

package test.classloader;
public class MockResponse {

public void print() {
System.out.println("hello world");
}
}

package test.classloader;
public class MockServlet {

public static void main(String[] args) {
MockResponse response = new MockResponse();

WebAppClassLoader.printClassLoaderHierarchy(response.getClass());
response.print();
}
}


[quote="WebAppClassLoader中的Main方法输出"]
------------- bootstrap: sun.boot.class.path -------------
D:\Program Files\Java\jdk1.6.0_18\jre\lib\resources.jar
D:\Program Files\Java\jdk1.6.0_18\jre\lib\rt.jar
D:\Program Files\Java\jdk1.6.0_18\jre\lib\sunrsasign.jar
D:\Program Files\Java\jdk1.6.0_18\jre\lib\jsse.jar
D:\Program Files\Java\jdk1.6.0_18\jre\lib\jce.jar
D:\Program Files\Java\jdk1.6.0_18\jre\lib\charsets.jar
D:\Program Files\Java\jdk1.6.0_18\jre\classes

------------- extension: java.ext.dirs -------------
D:\Program Files\Java\jdk1.6.0_18\jre\lib\ext
C:\Windows\Sun\Java\lib\ext

------------- system: java.class.path -------------
D:\test-work\java\classes
D:\Program Files\eclipse\plugins\org.junit4_4.5.0.v20090824\junit.jar
D:\Program Files\eclipse\plugins\org.hamcrest.core_1.1.0.v20090501071000.jar

sun.misc.Launcher
此类的类加载器为“启动类加载器”,它是由虚拟机的本地代码实现,在Java程序中无法引用

java.lang.String
此类的类加载器为“启动类加载器”,它是由虚拟机的本地代码实现,在Java程序中无法引用

java.lang.Integer
此类的类加载器为“启动类加载器”,它是由虚拟机的本地代码实现,在Java程序中无法引用

int
此类的类加载器为“启动类加载器”,它是由虚拟机的本地代码实现,在Java程序中无法引用

java.util.ArrayList
此类的类加载器为“启动类加载器”,它是由虚拟机的本地代码实现,在Java程序中无法引用

test.classloader.WebAppClassLoader
类加载器层次结构依次由子到父
ClassLoader: sun.misc.Launcher$AppClassLoader@16f0472
ClassLoader: sun.misc.Launcher$ExtClassLoader@18d107f

test.classloader.MockServlet
类加载器层次结构依次由子到父
ClassLoader: java.net.URLClassLoader@12f0999
ClassLoader: sun.misc.Launcher$AppClassLoader@16f0472
ClassLoader: sun.misc.Launcher$ExtClassLoader@18d107f

test.classloader.MockResponse
类加载器层次结构依次由子到父
ClassLoader: java.net.URLClassLoader@12f0999
ClassLoader: sun.misc.Launcher$AppClassLoader@16f0472
ClassLoader: sun.misc.Launcher$ExtClassLoader@18d107f

hello world
测试结束

[/quote]
由程序输出可以到看到 java.lang.String、Integer 等类都是由“启动类加载器”所加载的,在Java程序中无法获得它的引用

WebAppClassLoader 是由“系统类加载器sun.misc.Launcher$AppClassLoader”所加载

MockServlet 与 MockResponse 是由 WebAppClassLoader 所加载
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值