先看下图:
有四个不同的类加载器:
1,启动类加载器:顾名思义,是一个在jvm启动时候的类加载器,由于此时要加载JAVA_HOME/lib中的类(也可以指定-Xbootclasspath为加载路径),所以这玩意不可能本身是java写就的,没错,它是用C++开发的。
2,扩展类加载器:由sun.misc.Launcher$ExtClassLoader实现,负责加载JAVA_HOME/lib/ext中的类;
3,应用程序类加载器:由sun.misc.Launcher$AppClassLoader实现,一般程序员所写的程序都是通过这个来默认加载的,这个加载器我们可以通过ClassLoader的getSystemClassLoader()来获得;
4,用户自定义类加载器:程序员自己通过继承ClassLoader来实现自己的类加载器;
import java.net.URL;
import java.net.URLClassLoader;
/*
分析BootstrapClassLoader/ExtClassLoader/AppClassLoader的加载路径
*
*/
public class ClassPath_of_Bootstrap_Ext_AppClassLoader
{
public static void main(String[] args)
{
System.out.println("BootstrapClassLoader 的加载路径: ");
URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
for(URL url : urls)
System.out.println(url);
System.out.println("----------------------------");
//取得扩展类加载器
URLClassLoader extClassLoader = (URLClassLoader)ClassLoader.getSystemClassLoader().getParent();
System.out.println(extClassLoader);
System.out.println("扩展类加载器 的加载路径: ");
urls = extClassLoader.getURLs();
for(URL url : urls)
System.out.println(url);
System.out.println("----------------------------");
//取得应用(系统)类加载器
URLClassLoader appClassLoader = (URLClassLoader)ClassLoader.getSystemClassLoader();
System.out.println(appClassLoader);
System.out.println("应用(系统)类加载器 的加载路径: ");
urls = appClassLoader.getURLs();
for(URL url : urls)
System.out.println(url);
System.out.println("----------------------------");
}
}
输出结果:
BootstrapClassLoader 的加载路径:
file:/C:/jdk1.7.0_01/jre/lib/resources.jar
file:/C:/jdk1.7.0_01/jre/lib/rt.jar
file:/C:/jdk1.7.0_01/jre/lib/sunrsasign.jar
file:/C:/jdk1.7.0_01/jre/lib/jsse.jar
file:/C:/jdk1.7.0_01/jre/lib/jce.jar
file:/C:/jdk1.7.0_01/jre/lib/charsets.jar
file:/C:/jdk1.7.0_01/jre/classes
----------------------------
sun.misc.Launcher$ExtClassLoader@3e389405
扩展类加载器 的加载路径:
file:/C:/jdk1.7.0_01/jre/lib/ext/dnsns.jar
file:/C:/jdk1.7.0_01/jre/lib/ext/localedata.jar
file:/C:/jdk1.7.0_01/jre/lib/ext/sunec.jar
file:/C:/jdk1.7.0_01/jre/lib/ext/sunjce_provider.jar
file:/C:/jdk1.7.0_01/jre/lib/ext/sunmscapi.jar
file:/C:/jdk1.7.0_01/jre/lib/ext/zipfs.jar
----------------------------
sun.misc.Launcher$AppClassLoader@a200d0c
应用(系统)类加载器 的加载路径:
file:/E:/JAVA/JVM_Class_Reflect_Thread/ClassPath_of_Bootstrap_Ext_AppClassLoader/bin/
----------------------------
ClassLoader抽象类的几个关键方法:
(1) loadClass
此方法负责加载指定名字的类,ClassLoader的实现方法为先从已经加载的类中寻找,如没有则继续从parent ClassLoader中寻找,如仍然没找到,则从System ClassLoader中寻找,最后再调用findClass方法来寻找,如要改变类的加载顺序,则可覆盖此方法
(2) findLoadedClass
此方法负责从当前ClassLoader实例对象的缓存中寻找已加载的类,调用的为native的方法。
(3) findClass
此方法直接抛出ClassNotFoundException,因此需要通过覆盖loadClass或此方法来以自定义的方式加载相应的类。
(4) findSystemClass
此方法负责从System ClassLoader中寻找类,如未找到,则继续从Bootstrap ClassLoader中寻找,如仍然为找到,则返回null。
(5) defineClass
此方法负责将二进制的字节码转换为Class对象
(6) resolveClass
此方法负责完成Class对象的链接,如已链接过,则会直接返回。
看了上面balabala一段,好,问题来了:
1,sun.misc.Launcher是什么gui?
2,各个不同的类加载器之间到底有啥优先级或是什么关系?
3,如果各个类加载器间是有顺序的,那是如何实现这种关系的?
4,自己实现一个类加载器到底有什么意义?
5,如果类加载的时候有优先级,这种是一定的?还是只是一种推荐的原则?
1:这是 JRE 类库,JDK 的 src 中没有,需要到下面这个地址去下载 OpenJDK 6 的源代码:
http://download.java.net/openjdk/jdk6/
压缩文件有 42.17MB,解压后有 254MB,拥有 28000 多个文件。
假如解压到 %OPENJDK6_HOME% 目录中,sun.misc.Launcher 位于 %OPENJDK6_HOME%/jdk/src/share/classes/sun/misc 目录里。
主要代码:
public class Launcher {
public Launcher() {
ExtClassLoader extclassloader;
try {
//初始化extension classloader
extclassloader = ExtClassLoader.getExtClassLoader();
} catch(IOException ioexception) {
throw new InternalError("Could not create extension class loader");
}
try {
//初始化system classloader,parent是extension classloader
loader = AppClassLoader.getAppClassLoader(extclassloader);
} catch(IOException ioexception1) {
throw new InternalError("Could not create application class loader");
}
//将system classloader设置成当前线程的context classloader(将在后面加以介绍)
Thread.currentThread().setContextClassLoader(loader);
......
}
public ClassLoader getClassLoader() {
//返回system classloader
return loader;
}
}
extension classloader的部分代码:
static class Launcher$ExtClassLoader extends URLClassLoader {
public static Launcher$ExtClassLoader getExtClassLoader()
throws IOException
{
File afile[] = getExtDirs();
return (Launcher$ExtClassLoader)AccessController.doPrivileged(new Launcher$1(afile));
}
private static File[] getExtDirs() {
//获得系统属性“java.ext.dirs”
String s = System.getProperty("java.ext.dirs");
File afile[];
if(s != null) {
StringTokenizer stringtokenizer = new StringTokenizer(s, File.pathSeparator);
int i = stringtokenizer.countTokens();
afile = new File;
for(int j = 0; j < i; j++)
afile[j] = new File(stringtokenizer.nextToken());
} else {
afile = new File[0];
}
return afile;
}
}
system classloader的部分代码:
static class Launcher$AppClassLoader extends URLClassLoader
{
public static ClassLoader getAppClassLoader(ClassLoader classloader)
throws IOException
{
//获得系统属性“java.class.path”
String s = System.getProperty("java.class.path");
File afile[] = s != null ? Launcher.access$200(s) : new File[0];
return (Launcher$AppClassLoader)AccessController.doPrivileged(new Launcher$2(s, afile, classloader));
}
}
extension classloader是使用系统属性“java.ext.dirs”设置类搜索路径的,并且没有parent。
system classloader是使用系统属性“java.class.path”设置类搜索路径的,并且有一个parent classloader。
Launcher初始化extension classloader,system classloader,并将system classloader设置成为context classloader,但是仅仅返回system classloader给JVM。
2:其实类加载器间有树状关系,这种关系又称为双亲委派模型,就是要求除了顶层的启动类加载器外,其余的类加载器都应当有自己的父加载器,当收到加载类的请求的时候,本身的类加载器不会自己去尝试加载这个类,而是把这个请求委派(代理)父类去加载,直到父加载器无法完成这个加载请求时,子加载器才会尝试自己去加载,但要注意的是,类加载器的父子概念不是通过继承来实现的,而是通过组合来复用父加载器代码(不明白是吧?下面要看一下loadClass方法的实现就可以了)
组合实现父子关系的解析:
父子关系在ClassLoader的实现中有一个 ClassLoader类型的属性,我们可以在自己实现自定义的ClassLoader的时候初始化定义,而这三个系统定义的ClassLoader的父子关系分别是AppClassLoader————》(Parent)ExtClassLoader————————》(parent)BootClassLoader(null c++实现),就是为什么打印出来的ExtClassLoader的parent是null的原因,因为C++实现的bootClassLoader不算是java类
3:
protected synchronized Class<?> loadClass(String name,boolean resolve) throws ClassNotFoundException {
//检查请求的类是否已经被加载过了
//JVM判断类是否被加载有两个条件:完整类名是否一样、ClassLoader是否是同一个。
Class c = findLoadedClass(name);
if(c == null){
try{
if(parent != null){
c = parent.loadClass(name,false);//丢给它爹
}else{
c = findBootstrapClassOrNull(name);//如果没爹,丢给老祖宗
}
}catch(ClassNotFoundException e){
//说明父类无法完成加载
}
if(c == null){ //只好自己加载了
c = find(name);
}
......
}
}
问题又来了:
6,什么时候会父加载器会加载失败?
暂时理解为每个类加载器都有一个指定的加载范围,也就是一个指定的加载目录,所以如果该请求加载的类超出了自己本身的加载目录范围,这个类加载器就会无能为力,让最初的子类自己加载了。
5:一般来说,父类优先的策略就足够好了。在某些情况下,可能需要采取相反的策略,即先尝试自己加载,找不到的时候再代理给父类加载器。这种做法在Java的Web容器中比较常见,也是Servlet规范推荐的做法。比如,Apache Tomcat为每个Web应用都提供一个独立的类加载器,使用的就是自己优先加载的策略。IBM WebSphere Application Server则允许Web应用选择类加载器使用的策略。
一个典型的例子是JNDI,JDBC,JCE,JAXB,JBI这些服务,因为需要调用由独立厂商实现并部署在应用程序的classPath下的JNDI提供者代码,就会出现父类加载器请求子类加载器去完成类加载动作。
热部署OSGI(未了解)
4,自己实现类加载器
类加载器及测试代码如下:
public class MyClassLoader extends ClassLoader {
private String path = "c:/bin/";
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
String namePath = name.replace(".", "/");
String classPath = path + namePath + ".class";
InputStream is = null;
ByteArrayOutputStream os = null;
try {
is = new FileInputStream(new File(classPath));
os = new ByteArrayOutputStream();
int b = 0;
while ((b = is.read()) != -1) {
os.write(b);
}
byte[] bytes = os.toByteArray();
os.flush();
return defineClass(name, bytes, 0, bytes.length);
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
if(os!=null)
os.close();
if(is!=null)
is.close();
} catch (IOException e1) {
os=null;
is = null;
}
}
return null;
}
@SuppressWarnings("unchecked")
public static void main(String[] args) {
MyClassLoader myLoader = new MyClassLoader();
try {
Class myClass = myLoader.loadClass("com.ldh.loader.HelloWorld");
Object obj = myClass.newInstance();
Method method = myClass.getMethod("say", null);
method.invoke(obj, null);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class HelloWorld {
public void say(){
System.out.println("hello,world");
}
}
HelloWorld类放在c:/bin/目录下.
最后谈一下loadClass()和forName()的区别.
从上可以看出调用loadClass(name),相当于调用loadClass(name,false),表示只加载类,不连接初始化类,调用newInstance()才真正完成连接初始化操作.
Class.forName("xxxx")等同于Class.forName("xxxx",true,loader).true,表示载入实例的同时也载入静态初始化区块;false,表示只会加载该类别,但不会调用其静态初始化区块,只有等到整个程序第一次实例化某个类时,静态初始化区块才会被调用
在大多情况下loadClass()和forName()可以互用, 可以把ClassLoader.loadClass()看成是更底层的操作.在某些必须初始化类的场合,比如加载JDBC驱动,只能使用forName()方法了
资料:
http://blog.csdn.net/vking_wang/article/details/17162327
http://www.infoq.com/cn/articles/cf-Java-class-loader/
http://blog.csdn.net/irelandken/article/details/7048817
http://www.cnblogs.com/mailingfeng/archive/2012/07/02/2573419.html
http://xjtom78.iteye.com/blog/898882
http://liudeh-009.iteye.com/blog/1463388
- 大小: 2.6 MB
分享到:
相关推荐
首先,我们来了解一下ClassLoader的基本层次结构。在Java中,ClassLoader分为三个主要层次:Bootstrap ClassLoader、Extension ClassLoader和App ClassLoader(也称为System ClassLoader)。Bootstrap ClassLoader是...
本教程适合具备基本Java编程能力,包括创建、编译和执行简单的命令行Java程序,以及对类文件有基本理解的读者。完成教程后,你将能够: 1. 扩展JVM的功能性:了解如何利用ClassLoader实现特定的加载逻辑,例如动态...
下面我们将详细讨论ClassLoader的基本概念、工作流程以及如何自定义ClassLoader。 1. **ClassLoader的基本概念** - 类加载器是Java中的一个核心组件,它负责将类的.class文件加载到JVM中,并转换为可执行的Java...
ClassLoader的基本职责是根据类名动态加载对应的类文件。在Java中,类加载过程遵循双亲委派模型(Parent Delegation Model)。这意味着当一个ClassLoader尝试加载某个类时,它首先会将任务委托给其父ClassLoader。...
首先,我们来看ClassLoader的基本工作流程。当JVM启动时,它会有一个初始的Bootstrap ClassLoader,用于加载JRE核心类库。接着,Extension ClassLoader加载扩展类库,然后是App ClassLoader加载应用程序的类路径下的...
通过对`ClassLoader`的基本概念、工作原理、双亲委托机制以及不同类型的类加载器的了解,我们可以更好地理解Java类加载的过程及其背后的设计哲学。这对于深入理解Java虚拟机的内部机制以及开发高质量的应用程序都至...
首先,ClassLoader可以分为三种基本类型:Bootstrap ClassLoader、Extension ClassLoader和Application ClassLoader。Bootstrap ClassLoader是JVM启动时的第一个ClassLoader,负责加载JDK的`<JAVA_HOME>\lib`目录下...
一、ClassLoader的基本概念 Java程序由多个类组成,每个类对应一个.class文件。当程序运行时,ClassLoader根据需要动态加载这些类。Java提供了三种内置的ClassLoader: 1. BootstrapClassLoader(启动类加载器):...
深入理解ClassLoader的工作原理对于优化应用性能、解决类加载问题以及实现自定义加载策略至关重要。 首先,JVM启动时,会构建一个类加载器的层次结构,主要包括三个基本类加载器: 1. Bootstrap ClassLoader:引导...
首先,我们来理解ClassLoader的基本概念。ClassLoader是一个抽象的概念,它是Java中的一个接口,位于`java.lang.ClassLoader`包下。它的主要职责是找到类的二进制数据(.class文件),然后将其转换为Class对象,以便...
首先,ClassLoader的基本职责是加载.class文件,将字节码转化为运行时的Java对象。这个过程分为三个步骤:加载、验证和初始化。在Java中,类的加载是动态的,只有在被使用到时才会加载,这种机制叫做延迟加载或按需...
首先,让我们理解`ClassLoader`的基本概念。`ClassLoader`是Java中的一个接口,位于`java.lang`包下,它是Java运行时环境的一部分。它的主要职责是根据类的全名(包括包名)找到对应的`.class`文件,并将其转换为...
总结来说,Java的ClassLoader机制是Java平台的重要组成部分,理解它的运作方式对于深入学习Java和进行JVM相关的优化至关重要。无论是默认的三个类加载器,还是自定义类加载器,它们共同确保了Java程序的正常运行和...
通过本教程的学习,你将能够理解Java ClassLoader的基本概念及其工作原理,并学会如何创建自定义的ClassLoader。自定义ClassLoader不仅能够扩展JVM的功能,还能够在实际项目中解决特定问题,如动态加载远程资源、...
JVM 首先启动 bootstrap classloader,该 ClassLoader 加载 Java 核心 API,然后调用 ExtClassLoader 加载扩展 API,最后 AppClassLoader 加载 CLASSPATH 目录下定义的 Class,这就是一个程序最基本的加载流程。...
在Android系统中,Classloader(类加载器)是至关重要的组件,它负责查找并加载Java类到Dalvik或ART运行时环境。这个测试demo是为了帮助开发者深入理解Android中两种主要的类加载器:DexClassLoader和...
了解和掌握ClassLoader的工作原理以及如何自定义ClassLoader对于深入理解Java应用程序的运行机制非常有帮助。以下是对ClassLoader API的使用和自定义的详细说明。 首先,我们来看ClassLoader的基本概念。在Java中,...
理解`Classloader`的工作原理对于深入学习Java、优化应用性能以及进行安全控制具有重要意义。 首先,让我们来看看`Classloader`的基本结构。Java中的类加载器通常遵循一种委托模型(Delegation Model)。这意味着当...