`
panshaobinSB
  • 浏览: 198376 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

classLoader的基本理解

阅读更多
先看下图:


有四个不同的类加载器:
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虚拟机类加载机制,对于深入理解jvm工作原理有很好的帮助作用,对于初学java,有一定工作经验的小伙伴来说是一本提高自身java素养,夯实自己java基本技能的“葵花宝典”。

    全面解析Java类加载器

     java.lang.ClassLoader类的基本职责是根据一个指定的类的名称,找到或者生成其对应的字节代码,然后从这些字节代码中定义出一个Java 类,即 java.lang.Class类的一个实例。  ClassLoader提供了一系列的方法,...

    Java高级工程师简历模板18k+

    基本资料 教育背景 求职意向 专业技能 项目经验 工作经历 自我评价 ◆专业技能 1.具有扎实的Java基础,对面向对象编程有深刻的理解,熟练掌握java IO流、集合、多线程、反射,泛型,注解,网络编程等基础...

    scala-bytecode:用Scala编写的JVM字节码理解框架

    Scala中的简洁字节码分析,理解和优化。 构建在ObjectWeb ASM 5.0之上。 罐中的字节码 ####注意: 这项工作尚未完成。 请谨慎行事。 scala.bytecode 用于使用ASM加载和修改字节码数据结构的基本软件包。 主...

    JavaSetup8u101.zip 编程工具

    另一方面,Java 丢弃了 C++ 中很少使用的、很难理解的、令人迷惑的那些特性,如操作符重载、多继承、自动的强制类型转换。特别地,Java 语言不使用指针,而是引用。并提供了自动分配和回收内存空间,使得程序员不必...

    深入分析Java Web技术内幕 修订版

    其次深入介绍了Java 技术,包括I/O 技术、中文编码问题、Javac 编译原理、class 文件结构解析、ClassLoader 工作机制及JVM 的内存管理等。最后介绍了Java 服务端技术,主要包括Servlet、Session 与Cookie、Tomcat 与...

    Java 基础面试题

    3. 八大基本数据类型,所占字节数 4. List、Set、Map的区别 5. 什么情况下使用List、Map、Set? 6. ArrayList和LinckedList 7. LinckedList底层 8. ArrayList底层 9. mybatis的SqlSession如何保证线程安全 10...

    AndroidComponentPlugin:Android上简单实现四大组件的插件化,供学习使用

    Android上简单实现四大组件的插件化说明:此项目仅用于学习插件化基本的实现思路,在此基础上学习理解四大组件的运行机制。实现插件化的重点在于对Android四大组件和资源加载流程的分析和解读。插件化代码的编写,...

    基于JAVA的学生通讯录管理系统设计和实现[文献综述].doc

    除了Java语言具有的许多安全特性以外,Java对通过网络下载 的类具有一个安全防范机制(类ClassLoader),如分配不同的名字空间以防替代本地的 同名类、字节代码检查,并提供安全管理机制让Java应用设置安全哨兵。...

    java面试题,180多页,绝对良心制作,欢迎点评,涵盖各种知识点,排版优美,阅读舒心

    【反射】反射中,Class.forName和classloader的区别 42 【JVM】JAVA编译原理和JVM原理 42 【JVM】Java内存模型 44 【JVM】jvm内存模型 45 主内存与工作内存 45 内存间交互操作 46 重排序 48 【JVM】内存泄漏 49 ...

    Spring.3.x企业应用开发实战(完整版).part2

    3.2.2 类装载器ClassLoader 3.2.3 Java反射机制 3.3 资源访问利器 3.3.1 资源抽象接口 3.3.2 资源加载 3.4 BeanFactory和ApplicationContext 3.4.1 BeanFactory介绍 3.4.2 ApplicationContext介绍 3.4.3 父子容器 ...

    Spring3.x企业应用开发实战(完整版) part1

    3.2.2 类装载器ClassLoader 3.2.3 Java反射机制 3.3 资源访问利器 3.3.1 资源抽象接口 3.3.2 资源加载 3.4 BeanFactory和ApplicationContext 3.4.1 BeanFactory介绍 3.4.2 ApplicationContext介绍 3.4.3 父子容器 ...

    Java经典入门教程pdf完整版

    这些类 Smartcard版本只支持 Boolean与Bytc这两种Java基本类,此版本定位在 SmartCard 的应用上 四:闲话 ava 1:Java历史 在上世纪90年代初,sun公司有一个叫做 Green的项目,目的是为家用消费电子广品 开发一个分布式...

Global site tag (gtag.js) - Google Analytics