java动态加载

标签: java 动态加载 | 发表时间:2014-10-09 22:07 | 作者:qian0021514578
出处:http://www.iteye.com
第一部分:Java虚拟机启动时,关于类加载方面的一些动作
当使用java ProgramName.class运行程序时,Java找到JRE,接着找到jvm.dll,把该动态库载入内存,这就是JVM。然后加载其它动态库, 并激活JVM。JVM激活之后会进行一些初始化工作,之后生成BootstrapLoader,该Class Loader是由C++写的。BootstrapLoader加载Launcher.java中的ExtClassLoader,并设定其Parent为 null,意思是ExtClassLoader的Parent Class Loader就是BootstrapLoader。然后BootstrapLoader又加载Launcher.java中的 AppClassLoade,并设定其Parent Class Loader是ExtClassLoader。不过如果调用ExtClassLoader的getParent( )方法,则返回的是null。这两个Class Loader都是以静态类的形式存在,并且都是用Java编写的。
这三个Class Loader它们都有自己的类查找路径:
BootstrapLoader: sun.boot.class.path
ExtClassLoader: java.ext.dirs
AppClassLoader: java.class.path
以上三个路径都是Java的系统属性,可以通过System.getProperty(String key)方法来查看其设置:
System.out.println(System.getProperty("java.class.path"));
 
现在看看每一种属性输出后的值,从而可以发现每种Class Loader分别负责哪些类的装载:
key: sun.boot.class.path
String: %JAVA_HOME%/lib/resources.jar, rt.jar, sunrsasign.jar, jsse.jar, jce.jar, charsets.jar,
          %JAVA_HOME%/classes
 
key: java.ext.dirs
String: %JAVA_HOME%/lib/ext, %Windows%/sun/java/lib/ext
 
key: java.class.path
String: 程序的入口文件类所在的目录
 
由此可见,BootstrapLoader负责Java核心类(所有以java.*开头的类)。ExtClassLoader负责加载扩展类(所以以javax.*开头的类以及存在ext目录下的类)。AppClassLoader负责加载应用程序自身的类。
 
第二部分:Java的类加载机制。Java是如何加载类的,其流程。
类加载按照加载时机,是否自动加载分为两种:预先加载和按需加载。
预先加载的类是在JVM启动之后,应用程序运行之前。至少包含rt.jar中的所有类。
按需加载则是在应用程序运行之后,在程序运行过程中,JVM遇到一个还未被装载的类,这时由Class Loader把该类载入内存。
 
类加载按照方式来分,也是两种:隐式加载和显式加载。
隐式加载是通过new的方式,在类初始化时由JVM根据相应的Class Loader将类载入。
显式加载则是程序员在代码中显式利用某个Class Loader将类载入。
 
JVM自动装载类的算法是这样的:如果Class A的实例引用了Class B的实例,则在默认情况下,JVM会先找到Class A的Class Loader,然后用该Class Loader来装载Class B。
 
Class Loader装载类的一般算法如下:
Background: Class Loader是按照层次关系组织起来的,每一个Class Loader都有一个Parent。如果在创建Class Loader时不显式指定其父Class Loader,JVM会把系统Class Loader指定为该Class Loader的Parent。每一个Class Loader都有自己对应的Loaded Class Cache,换句话说,Loaded Class Cache由两部分组成:ClassLoader,以及由它加载的Class类名。
  1. 检查这个类是否已经被加载进去了
  2. 如果还没有加载,调用父对象加载该类
  3. 如果父对象无法加载,调用本对象的findClass()取得这个类。
所以当创建自己的Class Loader时,只需要重载findClass()方法。
Java 1.2之后,类的装载采用委托模式。
一个已经加载的类是无法被更新的,如果试图用同一个ClassLoader再次加载 同一个类,会得到异常java.lang.LinkageError: duplicate class definition。只能够重新创建一个新的ClassLoader实例来再次加载新类。
 
第三部分:定义自己的Class Loader
为什么要使用自己的ClassLoader?
因为JVM自带的ClassLoader只是懂得从本地文件系统加载标准的java class文件,如果编写自己的ClassLoader,可以
  1. 在执行非置信代码之前,自动验证数字签名
  2. 动态地创建符合用户特定需要的定制化构建类
  3. 从特定的场所取得java class,例如数据库和网络。

当创建自己的ClassLoader时,需要继承java.lang.ClassLoader或者它的子类。在实例化每个ClassLoader对 象时,需要指定一个父对象;如果没有的话,系统自动指定ClassLoader.getSystemClassLoader()为父对象。

第四部分:程序中显示加载并实例化类的几种方式:
1) 使用Class类
Class foo = Class.forName(String ClassTypeName);  // 通过调用ClassLoa der.getCallerClassLoader( )得到当前Class Loader,然后查找并载入ClassTypeName。
or
Class foo = Class.forName(String ClassTypeName, boolean initialize, ClassLoader loader)  // 显式指定用哪个Class Loader来查找并载入ClassTypeName。
 
ClassTypeName boo = (ClassTypeName) foo.newInstance( );
 
2) 通过ClassLoader的子类针对不同情况装载类,比如java.net.URLClassLoader等。
 
获取当前ClassLoader的方法:
ClassLoader foo = Thread.currentTread().getCoontextClassLoader();

第五部分: Class.forName()与ClassLoader.loadClass()的区别

Class clazz = Class.forName("XXX.XXX");

ClassLoader cl = Thread.currentThread().getContextClassLoader();
Class clazz = cl.loadClass("XXX.XXX");
都可以装载一个类那么他们的区别是什么呢?
进一步研究Class.forName()是调用
Class.forName(name, initialize, loader); 也就是Class.forName("XXX.XXX"); 等同与 Class.forName("XXX.XXX", true, CALLCLASS.class.getClassLoader());

第二次参数表示装载类的时候是否初始化该类, 即调用类的静态块的语句及初始化静态成员变量。

Class clazz = cl.loadClass("XXX.XXX"); 没有指定是否初始化的选项。只有执行clazz.newInstance();时才能够初始化类。可以说 Class.forName("XXX.XXX", false, cl)执行过程是一致的。只是ClassLoader.loadClass()是更底 层的操作。

看一下JDBC驱动的装载。
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbcurl");
当调用Class.forName("com.mysql.jdbc.Driver");是Driver已经被初始化并注册到DriverManager中。MySQL Driver的代码
public class Driver extends NonRegisteringDriver
implements java.sql.Driver
{

public Driver()
throws SQLException
{
}

static 
{
try
{
DriverManager.registerDriver(new Driver());
}
catch(SQLException E)
{
throw new RuntimeException("Can't register driver!");
}
}
}
改修JDBC驱动的装载
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Class clazz = cl.loadClass("com.mysql.jdbc.Driver");
clazz.newInstance();
Connection conn = DriverManager.getConnection("jdbcurl");
同样可以执行。

进一步说:

Class .forName是从指定的classloader中装载类,如果没有指定,也就是一个参数的时候,是从装载当前对象实例所在的classloader中装载类.
而ClassLoader的实例调用loadclass方法,是指从当前ClassLoader实例中调用类, 而这个实例与装载当前所在类实例的Classloader也许不是同一个 .
举个例子吧, 有A,B , C两个ClassLoader , 当前运行的类D的实例是d(装载它的是A) ,  如果D中使用Class .forName那么就是使用的ClassLoader就是A,当然,也可以指定为B. 而如果D中代码找到的ClassLoader实例是C,那么就是用D来装载所指定的类.

为什么要用不同的ClassLoader 装载?
举例来说:如果在Class 被载入的过程中,你希望使用在自己的Class Loader来实现特定的操作,请使用ClassLoader方式。

貌似CGLib之类的bytecode generation框架很多地方会使用指定特殊ClassLoader的方式。
使用多个classloader的情况非常常见,比如说我们的app server,那么都是这样的. 在Web与EJB间, 他们的classLoader就是不同的,这样做的目的就是为了避免两者间类装载的相互干扰.

再举个例子:
Static初始化区块在什么时候被调用的问题?
Public A{Static{System.out.println(“HaHaHa”);}}
Class.forName(“A”);
Class.forName(“A”,false, ClassLoader.getSystemClassLoader());
看看奥妙在哪里?Java ClassLoader机制和原理又是如何?
程序示例:
public class A {
static { System.out.println("A`static is executed!");}
public A() {System.out.println("A`construct is executed!");}
public void show(){System.out.println("A`method is executed!");}
}
调用程序1:
Class c = Class.forName("A");
Method m = c.getMethod("show", new Class[0]);
System.out.println("A`test is executed!");
Object obj = c.newInstance();
m.invoke(obj, new Object[0]);
执行结果:
A`static is executed!
A`test is executed!
A`construct is executed!
A`method is executed!
调用程序2:
Class c = ClassLoader.getSystemClassLoader().loadClass("A");
System.out.println("A`test is executed!");
Method m = c.getMethod("show", new Class[0]);
Object obj = c.newInstance();
m.invoke(obj, new Object[0]);
执行结果:
A`test is executed!
A`static is executed!
A`construct is executed!
A`method is executed!
可见执行顺序为先执行 static{}块中的代码,然后执行构造函数,之后才是方法的调用。
classloader的两种载入方式:
1)pre-loading预先载入,载入基础 类
2)load-on-demand按需求载入
java动态载入class的两种方式:
1)implic it隐式,即利用实例化才载入的特性来动态载入class
2)explic it显式方式,又分两种方式:
a)java.lang.Class的forName()方法  (上述调用程序1采用此方式载入)
b)java.lang.ClassLoader的loadClass()方法(上述调用程序2采用此方式载入)
static块在什么时候执行?
当调用forName(String)载入class时执行, ( 这个过程在类的所有父类中递归地调用)
如果调用ClassLoader.loadClass并不会执行.
forName(String,false,ClassLoader)时也不会执行.
如果载入Class时没有执行static块则在第一次实例化时执行.比如new ,Class.newInstance()操作
static块仅执行一次


已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [java 动态加载] 推荐:

java动态加载

- - Java - 编程语言 - ITeye博客
第一部分:Java虚拟机启动时,关于类加载方面的一些动作. 当使用java ProgramName.class运行程序时,Java找到JRE,接着找到jvm.dll,把该动态库载入内存,这就是JVM. 然后加载其它动态库, 并激活JVM. JVM激活之后会进行一些初始化工作,之后生成BootstrapLoader,该Class Loader是由C++写的.

动态加载HQL

- senyo - BlogJava-首页技术区
Java代码如下:(ReloadableDynamicHibernate.java). 135         private Map qlMap;                //查询的映射. 这样就实现了每次修改SQL or HQL语句后不用重启服务器,立刻看到结果,加快了开发速度.

无刷新动态加载数据,滚动条加载

- - CSDN博客Web前端推荐文章
无刷新动态加载数据,滚动条加载.  滚屏加载更多数据,适合评论等页面. 本例的数据库很简单,一看就明了.     var winH = $(window).height(); //页面可视区域高度  .         var scrollT = $(window).scrollTop(); //滚动条top  .

美团Android DEX自动拆包及动态加载简介

- - 美团技术团队
作为一个android开发者,在开发应用时,随着业务规模发展到一定程度,不断地加入新功能、添加新的类库,代码在急剧的膨胀,相应的apk包的大小也急剧增加, 那么终有一天,你会不幸遇到这个错误:. 生成的apk在android 2.3或之前的机器上无法安装,提示INSTALL_FAILED_DEXOPT.

Android 动态加载技术三个关键问题详解

- - 移动开发 - ITeye博客
关注微信号:javalearns   随时随地学Java. 动态加载技术(也叫插件化技术)在技术驱动型的公司中扮演着相当重要的角色,当项目越来越庞大的时候,需要通过插件化来减轻应用的内存和CPU占用,还可以实现热插拔,即在不发布新版本的情况下更新某些模块. 动态加载是一项很复杂的技术,这里主要介绍动态加载技术中的三个基础性问题,至于完整的动态加载技术的实现请参考笔者发起的开源插件化框架DL:.

Apache工具类动态加载资源文件

- - 编程语言 - ITeye博客
    利用Apache的commons-configuration-1.6.jar 工具类可以实现动态加载XMl,Properties文件,加载原理后续补上. //休息10秒后重新加载配置文件.    src/resource/config.xml的路内容是:. 李四. 已有 0 人发表留言,猛击->> 这里<<-参与讨论.

Android 使用动态加载框架DL进行插件化开发

- - CSDN博客移动开发推荐文章
如有转载,请声明出处: 时之沙:  http://blog.csdn.net/t12x3456    (来自时之沙的csdn博客).         随着应用的不断迭代,应用的体积不断增大,项目越来越臃肿,冗余增加.项目新功能的添加,无法确定与用户匹配性,发生严重异常往往牵一发而动全身,只能紧急发布补丁版本,强制用户进行更新.结果频繁的更新,反而容易降低用户使用黏性.或者是公司业务的不断发展,同系的应用越来越多,传统方式需要通过用户量最大的主项目进行引导下载并安装..

Android插件化(一):使用改进的MultiDex动态加载assets中的apk

- - CSDN博客推荐文章
Android插件化(一):使用改进的MultiDex动态加载assets中的apk. 为了解决65535方法数超标的问题,Google推荐使用MultiDex来加载classes2.dex,classes3.dex等等,其基本思想就是在运行时动态修改ClassLoader,以达到动态加载类的目的.

[已实现] iview-admin动态加载路由修改方法 · Issue #1158 · iview/iview-admin · GitHub

- -
本示例基于iview-admin的template分支修改,其他分支应该也是支持的,通过mock方式模拟后台数据,将mock数据更改为自己的后端数据格式,转换方法稍加修改即可. @/mock/login.js中增加数据. 直接将@router/routers.js中,home节点到404节点之间的数据 __剪切 __过去即可.

Java中的锁(Locks in Java)

- - 并发编程网 - ifeve.com
原文链接 作者:Jakob Jenkov 译者:申章 校对:丁一. 锁像synchronized同步块一样,是一种线程同步机制,但比Java中的synchronized同步块更复杂. 因为锁(以及其它更高级的线程同步机制)是由synchronized同步块的方式实现的,所以我们还不能完全摆脱synchronized关键字( 译者注:这说的是Java 5之前的情况).