新的起点 新的开始

快乐生活 !

通过ClassLoader说明容器热部署实现机制

    在J2EE的项目中,容器给我们提供的热部署功能使得我们不用重启动容器而修改我们的代码。比如使用Weblogic,我们可以在Weblogic-application.xml中配置是否支持热部署Servlet。查阅Weblogc 文档,其实在Weblogic中,EJB组件也是可以热部署的,但如何要热部署EJB组件,Weblogc要求必须自定义ClassLoder。
    JVM规范中没有指定JVM支持动态加载修改过的类。类的加载,卸载对于程序员是透明的。如果我们要实现类的动态加载我们就要理解JVM本身类的加载与卸载的原理,实现热部署。对于JVM加载类方面的资料在网上很多的,在这里我做简单概述:
    (1)JVM加载时通过ClassLoader加载的。
    (2)JVM有3层继承关系的ClassLoder 分别是:
                                       -----BootStrap类加载器 加载JRE/lib
                                                -----------------ExtClassLoader 加载 JRE/lib/ext
                                                           ----------AppClassLoader 加载ClassPath/
    (3)为了安全性,JVM加载采用了双亲委派机制,如何理解呢,就是当需要加载一个类时,当前的ClassLoader先请求父ClassLoader,依次
      类推,直到父类的ClassLoader无法加载时,才通过当前的ClassLoser加载,这就保证了像String这样的类型必须使用JRE里面的, 使得
      JRE lib 下类不会被修改。同时避免了ClassCaseException。
   (4)在JVM中,一个实例是通过本身的类名+加载它的ClassLoader识别的,也就是说 不同的ClassLoader 加载同一个类在JVM是不同的。
   (5)同一个ClassLoader是不允许多次加载一个类的,否则会报java.lang.LinkageError。attempted  duplicate class definition for
       name XXX,在下面的例子中会指出。
    既然JVM不支持热部署,那么要实现热部署,就必须自定义ClassLoader,当类被修改过后,加载该类。下面通过代码说明:
package classloader;

/**
 * 
@author vma
 
*/
// 自定义一个类加载器
public class DynamicClassLoader extends ClassLoader {
    
  
    
public Class<?> findClass(byte[] b) throws ClassNotFoundException {

        
return defineClass(null, b, 0, b.length);
    }


package classloader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
/**
 * 
@author vma
 
*/
public class ManageClassLoader {
    DynamicClassLoader dc 
=null;
    
    Long lastModified 
= 0l;
    Class c 
= null;
    //加载类, 如果类文件修改过加载,如果没有修改,返回当前的
    
public Class loadClass(String name) throws ClassNotFoundException, IOException{
     
if (isClassModified(name)){
        dc 
=  new DynamicClassLoader();
      
return c = dc.findClass(getBytes(name));
     }
     
return c;
    }
    //判断是否被修改过
    
private boolean isClassModified(String filename) {
        
boolean returnValue = false;
        File file 
= new File(filename);
        
if (file.lastModified() > lastModified) {
            returnValue 
= true;
        }
        
return returnValue;
    }
       // 从本地读取文件
       
private byte[] getBytes(String filename) throws IOException {
        File file 
= new File(filename);
        
long len = file.length();
        lastModified 
= file.lastModified();
        
byte raw[] = new byte[(int) len];
        FileInputStream fin 
= new FileInputStream(file);
        
int r = fin.read(raw);
        
if (r != len) {
            
throw new IOException("Can't read all, " + r + " != " + len);
        }
        fin.close();
        
return raw;
    }
}
测试类;Main 每隔 5s 加载一次

package classloader;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 *
 * 
@author vma
 
*/
public class Main {

    
/**
     * 
@param args the command line arguments
     
*/
    
public static void main(String[] args) throws ClassNotFoundException, IOException, NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException, InterruptedException {
        String path 
= "D:\\deploy\\JDBC\\ClassLoader\\build\\classes\\classloader\\LocalClass.class";
        ManageClassLoader mc 
= new ManageClassLoader();
        
while(true){
       Class c 
= mc.loadClass(path);
        Object o 
= c.newInstance();
       Method m 
= c.getMethod("getName");
        m.invoke(o);
        System.out.println(c.getClassLoader());
        Thread.sleep(
5000);
        }


    }

被加载的类
/**
 *
 * 
@author vma
 
*/
public class LocalClass {

    
public void getName() {
        
      System.out.println(
"hahaha ");
    }
}

运行时,每隔5s 输出:
hahaha
classloader.DynamicClassLoader@61de33
当我们修改 System.out.println("hahaha "); ---> System.out.println("changed  "); 编译LocalClass后
输出变为:
changed
classloader.DynamicClassLoader@173a10f

loadClass中, 我们必须重新初始化一个ClassLoader,负责就会违背同一个ClassLoader是不允许多次加载一个类的。
    public Class loadClass(String name) throws ClassNotFoundException, IOException{
     
if (isClassModified(name)){
        dc 
=  new DynamicClassLoader();
      
return c = dc.findClass(getBytes(name));
     }
     
return c;
    }

当然,容器的实现机制肯定及其完善,不可能周期性的加载,可能回通过监听机制,动态加载修改过的类。但它的实现机制肯定也是重新
实例化一个ClassLoder,加载需要加载的类。






 



posted on 2008-08-30 23:36 advincenting 阅读(11406) 评论(8)  编辑  收藏

评论

# re: 通过ClassLoader说明容器热部署实现机制 2008-08-31 12:11 寻道者

好。  回复  更多评论   

# re: 通过ClassLoader说明容器热部署实现机制 2008-08-31 20:55 BeanSoft

收藏了!  回复  更多评论   

# re: 通过ClassLoader说明容器热部署实现机制 2008-09-01 08:59 蓝剑

很好,收藏!谢谢  回复  更多评论   

# re: 通过ClassLoader说明容器热部署实现机制 2008-09-01 09:51 CowNew开源团队

最近一年一直在搞.net的东西,.net里连一个热加载机制都不支持,微软还一大堆借口,好怀念Java。  回复  更多评论   

# re: 通过ClassLoader说明容器热部署实现机制 2008-09-03 09:54 usomething

兄弟,想问一句,你的classLoader支不支持加载修改后的jar?  回复  更多评论   

# re: 通过ClassLoader说明容器热部署实现机制 2008-09-03 14:12 advincenting

在这里,我仅仅是举例子来说明如何热部署,你当然可以定制一个Jar的加载类,动态加载卸载就可以了。  回复  更多评论   

# re: 通过ClassLoader说明容器热部署实现机制 2008-10-08 10:54 freeman

楼主,我有个问题,假如LocalClass里面又引用了其它的类如Person类
那我们就必须先加载Person类,再加载LocalClass,这样new LocalClass的时候才不会出错,我的问题是自己定义的Classloader为什么不会自动加载Person类呢?
public class LocalClass {

public void getName() {
Person person =new Person();
person.setName("freeman");
System.out.println("hahaha ---------++-----"+person.getName());
}
}  回复  更多评论   

# re: 通过ClassLoader说明容器热部署实现机制 2008-10-09 09:26 freeman

上面的问题,我自己解决了,是因为我程序里写的有问题,结果Person类没有加载进去的原因,只要在用LocalClass之前把Person类加载进来就行了。

但我现在又遇到新问题了,就是如果LocalClass 如果继承某个父类,那父类一定要在LocalClass之前加载,如何能解决这个限制啊,我想达到,只要在用LocalClass类实例对象之前加载了相关类就行了,不要一定非要我手功指定顺序加载,那位帅哥搞过啊!!  回复  更多评论   


只有注册用户登录后才能发表评论。


网站导航:
 

公告

Locations of visitors to this page

导航

<2008年9月>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

统计

常用链接

留言簿(13)

随笔分类(71)

随笔档案(179)

文章档案(13)

新闻分类

IT人的英语学习网站

JAVA站点

优秀个人博客链接

官网学习站点

生活工作站点

最新随笔

搜索

积分与排名

最新评论

阅读排行榜

评论排行榜