Java的类加载器简述

类加载器是java设计中一项创新,独立于JVM之外,类如何加载可以由JVM外部实现。如早期的Applets,曾经大红的OSGI等。

类加载器就是加载类的。一个jvm中可以存在若干个类加载器。相同的字节码因为类加载器不同,产生的实例肯定不同,即用instanceof 或者equal,isInstance()等方法对较相同字节码不同类加载器对象时返回均为false。

开发者可以继承java.lang.ClassLoader重写findClass(String clazz)或者loadClass(String clazz)实现自定义的ClassLoader。

*双亲委派模型
从开发者角度,类加载器有Bootstrap ClassLoader,Extension ClassLoader,System ClassLoader,self-defined ClassLoader.
其中BootstrapClassLoader用来加载$JAVA_HOME/lib下或-Xbootclasspath指定的核心库,其中包括java.lang下所有类。
ExtensionClassLoader加载$JAVA_HOME/lib/ext或者java.ext.dirs环境变量下类库。
SystemClassLoader为开发者程序默认加载器,用于加载ClassPaht下类库。

自jdk1.2以后,JVM加载类字节码时,采用双亲委派模型,即除了Bootstrap ClassLoader之外其他类加载器均有自己的父类加载器。一个类加载器加载一个字节码class时,首先把请求委派给父类加载器,每个加载器都这么干,如果没找到,则继续委派给Bootstrap类加载器。还没找到,才去自己找。通过该设计,其实维护了一种加载时的优先级关系。java.lang下类始终由等级最高的BootstrapClassLoader加载,即jvm中何时也只会出现一个java.lang.Object类。即使开发者使用自定义ClassLoader使用defineClass强行加载时也会通不过安全检查。

**双亲委派模型之外的特例
Thread Context ClassLoader:
 主要用来解决Bootstrap ClassLoader在一些特定情况下如何加载SPI(Service Provider Interface)代码,如JNDI、JDBC、JCE等。SPI代码特殊性在于,SPI接口在java核心库提供,需要由bootstrap ClassLoader加载,而SPI实现则为第三方提供,按照上面的模型,启动类加载器无法识别第三方代码库。
线程上下文类加载器默认继承父线程的类加载器,不设置的话默认是system classLoader。所以SPI情况下都可通过线程上下文加载器加载字节码。

Class.forName方法
public static Class forName(String className) throws ClassNotFoundException;
看源码可以知道,该方法使用调用者的ClassLoader加载类,并初始化类。
而另外重载方法
public static Class forName(String name, boolean initialize,ClassLoader loader) throws ClassNotFoundException;
则可以定义是否初始化类,用什么ClassLoader加载类。
OSGI
Java 上的动态模块系统。它为开发人员提供了面向服务和基于组件的运行环境,并提供标准的方式用来管理软件的生命周期。典型产品就是Eclipse。
OSGI核心思想就是bundle。每个bundle由独立的ClassLoader加载,以java开头的包均由父类加载器加载,其他包则可以通过org.osgi.framework.bootdelegation参数定义由父类加载器加载。每个bundle可以定义需要import其他bundle的包或类,也可以定义需要export的包和类。这样bundle之间也可以建立类库的依赖关系。
OSGI通过bundle实现了组件或模块的热部署,需要更换bundle时,直接将bundle及其classloader换掉即可。通过OSGI提供的Service Ranking 思想,排名最高的服务被优先使用。而service rank值可以在注册bundle服务时指定。多么潇洒!

你可能感兴趣的:(深入java)