Java_动态重新加载Class机制

Java动态重新加载Class

项目中使用到了动态重新加载Class的机制,作用是让一些代码上线之前可以在线上环境测试一下,当然,这是非常不好的测试机制,我刚来的时候也为这种机制感到惊讶—怎么可以在线上环境运行测试代码!后来经过了解,这么做的原因有以下两个:

  • 有些代码没有办法在本地进行测试,本地没有线上的环境
  • 我们弱到连测试机都没有(这是重点)

既然我们连测试机都没有,那么我就觉得我们的项目其实也没有想象中的重要,这么测就这么测吧~~ 
    之前对ClassLoader没啥概念,google到一篇文章,翻译了一下并且做了一些补充,加深记忆 
原文地址:

引用

http://tutorials.jenkov.com/java-reflection/dynamic-class-loading-reloading.html#classloader

--------------------------------------------------- 
ClassLoader 
    顾名思义,ClassLoader就是用来Load Class的,当一个Class被加载的时候,这个Class所引用到的所有Class也会被加载,而且这种加载是递归的,也就是说,如果A引用到B,B 引用到C,那么当A被加载的时候,B也会被加载,而B被加载的时候,C也会加载。如此递归直到所有需要的Class都加载好。 
    常见的ClassLoader:

引用

* Bootstrap class loader:虚拟机运行时必须要用到的类的加载器,比如java.*。它通常是在虚拟机种用本地代码(如C)实现,在系统中用null表示。 
* Extension class loader:负责加载ext目录下的Class。 
* Application class loader:负责加载CLASSPATH上的类。

ClassLoader的代理层次关系 
    ClassLoader是以层次关系组织起来的,当你创建一个标准的Java ClassLoader的时候,你必须提供一个父ClassLoader。当一个ClassLoader需要加载一个Class的时候,它首先会让父 ClassLoader去加载这个Class,如果父ClassLoader不能加载这个Class,那么当前的ClassLoader才会自己去加载。 
    ClassLoader加载Class的步骤:

  • 检查这个Class是否已经被加载过了
  • 如果没有被加载过,那么让父ClassLoader尝试去加载
  • 如果父ClassLoader无法加载,那么尝试使用当前ClassLoader加载

从ClassLoader加载Class的步骤可以得知,如果你需要动态重新加载一个Class,那么你的ClassLoader必须跟上述标准流程有所区别,需要动态加载的Class不能交给父ClassLoader,否则你自己的ClassLoader将没有机会去加载这个Class(因为正常情况下父ClassLoader总是能加载到你所请求的Class)。 
    所以,如果你需要ClassLoader重新加载一个Class,重写findClass方法是起不到效果的,因为findClass在父 ClassLoader加载失败之后才会执行

Java代码  

  1. // First, check if the class has already been loaded
  2. Class c = findLoadedClass(name);
  3. if (c == null) {
  4. try {
  5. if (parent != null) {
  6. c = parent.loadClass(name, false);
  7. } else {
  8. c = findBootstrapClass0(name);
  9. }
  10. } catch (ClassNotFoundException e) {
  11. // If still not found, then invoke findClass in order
  12. // to find the class.
  13. c = findClass(name);
  14. }
  15. }

必须重写loadClass方法才能达到效果。

动态重新加载Class 
    Java内置的ClassLoader总会在加载一个Class之前检查这个Class是否已经被加载过,已经被加载过的Class不会加载第二次。因此要想重新加载Class,我们需要实现自己的ClassLoader。 
    另外一个问题是,每个被加载的Class都需要被链接(link),这是通过执行ClassLoader.resolve()来实现的,这个方法是 final的,因此无法重写。Resove()方法不允许一个ClassLoader实例link一个Class两次,因此,当你需要重新加载一个 Class的时候,你需要重新New一个你自己的ClassLoader实例。 
    刚才说到一个Class不能被一个ClassLoader实例加载两次,但是可以被不同的ClassLoader实例加载,这会带来新的问题:

Java代码  

  1. MyObject object = (MyObject)
  2. myClassReloadingFactory.newInstance("com.jenkov.MyObject");

这段代码会导致一个ClassCastException,因为在一个Java应用中,Class是根据它的全名(包名+类名)和加载它的 ClassLoader来唯一标识的。在上面的代码中object对象对应的Class和newInstance返回的实例对应的Class是有区别的:

  全名 ClassLoader实例
Object对象的Class com.jenkov.MyObject  AppClassLoader实例
newInstance返回对象的Class com.jenkov.MyObject 自定义ClassLoader实例

解决的办法是使用接口或者父类,只重新加载实现类或者子类即可。

Java代码  

  1. MyObjectInterface object = (MyObjectInterface)
  2. myClassReloadingFactory.newInstance("com.jenkov.MyObject");
  3. MyObjectSuperclass object = ( MyObjectSuperclass)
  4. myClassReloadingFactory.newInstance("com.jenkov.MyObject");

在自己实现的ClassLoader中,当需要加载MyObjectInterface或者MyObjectSuperclass的时候,要代理给父 ClassLoader去加载。

时间: 2024-10-07 18:32:37

Java_动态重新加载Class机制的相关文章

Silverlight项目笔记7:xml/json数据解析、MVVM下实现多级树形结构TreeView、忽视引用类型导致数据绑定错误、通过流或动态空间加载图片、虚拟目录设置、silverlight安全机制引发的问题、WebClient缓存问题

涉及的内容主要有: 1.xml/json数据解析 2.多级树形结构TreeView 3.忽视引用类型导致数据绑定错误 4.通过流或动态空间加载图片 5.虚拟目录设置 6.silverlight安全机制引发的问题 7.webclient缓存问题 1.xml/json数据解析 (1)xml数据解析 使用WebClient获取数据,获取到的数据实例化为一个XDocument,使用XDocument的Descendants(XName)方法获得对应节点的数据集合,再通过Element这个方法对数据集合进

JS获取图片的缩略图,并且动态的加载多张图片

找了好多资料也没有找到该死的ie的解决办法,最后放弃了ie <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>js获取缩略图</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <st

nginx php动态编译加载模块.

#Nginx动态编译加载模块步骤 #查看目前Nginx版本及编译模块 #[[email protected] ~]# /opt/app/lnmp/nginx-1.12.0/sbin/nginx -V #nginx version: nginx/1.12.0 #built by gcc 4.8.5 20150623 (Red Hat 4.8.5-11) (GCC) #built with OpenSSL 1.0.2k  26 Jan 2017 #TLS SNI support enabled #c

AutoSharedLibrary -- 基于模板元编程技术的跨平台C++动态链接加载库

基于模板元编程技术的跨平台C++动态链接加载库.通过模板技术,使用者仅需通过简单的宏,即可使编译器在编译期自动生成加载动态链接库导出符号的代码,无任何额外的运行时开销. ASL_LIBRARY_BEGIN(TestLib) ASL_SYMBOL(Proc_test1, test1, false) ASL_SYMBOL(Proc_test2, test2, true) ASL_LIBRARY_END() TestLib theLib; try { theLib.Load("./1.so"

esri-leaflet入门教程(5)- 动态要素加载

esri-leaflet入门教程(5)- 动态绘制图形 by 李远祥 在上一章节中已经说明了esr-leaflet是如何加载ArcGIS Server提供的各种服务,这些都是服务本身来决定的,API脚本只是非常简单的调用.但如果要做一列的地图交互操作或者动态渲染等,那就必须使用地图区域跳转.查询结果渲染.动态添加图形等多种交互手段.而这些交互手段基本上离不开一些非服务类型的数据加载,我们可以将其成为动态要素.动态要素一般是在页面端进行动态绘制的. 动态要素这一说法并不是ArcGIS 或者leaf

Bootstrap tab页的动态ajax加载

要实现这样的功能,点击tab导航ajax动态加载页面,当加载过之后,点击则不再重新加载,直接显示原来加载的页面. tab页面代码: <!-- Nav tabs --><ul class="nav nav-tabs" role="tablist" id="maintab">  <li class="active"><a href="#tab1"  onclick='s

Linux程序动态库加载优化

作者:zhanhailiang 日期:2014-10-26 linux程序动态库加载流程简介 linux从程序(program或对象)变成进程(process或进程),简单说来需要经过三步: fork进程,在内核创建进程相关内核项,加载进程可执行文件: 查找依赖的.so,逐一加载映射虚拟地址: 初始化程序变量: 如下例通过strace查看pwd命令执行过程: [root@~/wade/codeReview/learningc]# strace pwd execve("/bin/pwd"

php动态滚动加载实例

内容涉及:php.分页.jquery.div+css 实例下载:http://download.csdn.net/detail/roro5119/7373905 index.php <? //数据库配置文件 include("conn.php"); //默认搜索 $page = $_GET["page"] ? $_GET["page"] : 1; $pagesize = 20; $pageval = ($page-1)*20; $sql=&

linux和windows动态库加载路径区别

# linux和windows动态库加载路径区别 ### 简介------------------------------ linux加载动态库的路径是系统目录/lib和/usr/lib.- windows加载动态库的路径是本地目录下,然后再搜索windows/system和windows/system32目录 ### 备注------------------------------ linux加载动态库的路径方式,对于习惯windows开发的开发者是不太方便的.- 其实linux下可以设置从当