论坛登陆 用户名:  密 码:
设为首页  加入收藏
08年北京名校秋季招生
名牌院校免试入学宽进严出,突破考分限制,名校与你零距离,以下院校按报名先后顺序录取,24小时网上报名覆盖全国
  您现在的位置: 中国教育招生在线 >> IT >> JAVA认证 >> IT正文
避免重启你的应用程序 二
 作者:佚名     2007-3-14 16:30:00        来源:不详  浏览次数:

 

 

 

 

 

 

 

 

三:如果更新的功能包括应用逻辑,也就是class改变了,那就稍微麻烦点,你需要了解ClassLoader的原理。使用你定制的ClassLoader重新Load 已经编译好的class,就好比你重启应用一样。下面将简单介绍ClassLoader原理,以及举出一个例子来说明如何避免重启应用程序。
      虚拟机通过Classloader来转载类。bootstrap loader 负责load jdk的class(java.*,javax.*), system class loader是bootstrap的子类,负责load 所有在chasspath指定的变量。ClassLoader将字节流转化为Class类,这些字节流可能来源文件,也可能来源于网络或者数据库。转化方法是调用ClassLoader提供的final defineClass(className, classBytes, 0, classBytes.length)方法来实现。需要记住的是虚拟机里一个类的唯一标识是通过类的包名+类名+装载此类的ClassLoader。同一个ClassLoader实例只能装载Class一次,重复装载将抛出重复类定义异常。 
      如下自定义ClassLoader将从classpath里转载指定的类,来说明如上对ClassLoader的介绍,同时,我们用此ClassLoader演示如何避免重启动应用程序
      
public class DyLoader extends ClassLoader
{
    public DyLoader()
    {
        super(DyLoader.class.getClassLoader());
    }

    public Class loadFromCustomRepository(String className) {
    /**取环境变量*/
    String classPath = System.getProperty("java.class.path");
    List classRepository = new ArrayList();
    /**取得该路径下的所有文件夹 */
    if ( (classPath != null) && ! (classPath.equals(""))) {
      StringTokenizer tokenizer = new StringTokenizer(classPath,
          File.pathSeparator);
      while (tokenizer.hasMoreTokens()) {
        classRepository.add(tokenizer.nextToken());
      }
    }
    Iterator dirs = classRepository.iterator();
    byte[] classBytes = null;
    /**在类路径上查找该名称的类是否存在,如果不存在继续查找*/
    while (dirs.hasNext()) {
      String dir = (String) dirs.next();
      //replace '.' in the class name with File.separatorChar & append .class to the name
      String classFileName = className.replace('.', File.separatorChar);
      classFileName += ".class";
      try {
        File file = new File(dir + File.separatorChar + classFileName);
        if (file.exists()) {
          InputStream is = new FileInputStream(file);
          /**把文件读到字节文件*/
          classBytes = new byte[is.available()];
          is.read(classBytes);
          break;
        }
      }
      catch (IOException ex) {
        System.out.println("IOException raised while reading class file data");
        ex.printStackTrace();
        return null;
      }
    }
    return this.defineClass(className, classBytes, 0, classBytes.length);//加载类
  }

}

如下调用
DyLoader loader = new DyLoader();
Class a  = loader.loadFromCustomRepository("com.lijz.SampleDyService"); 
Class b = loader.loadFromCustomRepository("com.lijz.SampleDyService");
第三行代码将会抛出
java.lang.LinkageError: duplicate class definition: com/lijz/SampleDyService



如果如下调用,则一切正常,这是因为你使用新的ClassLoader实例来装载com.lijz.SampleDyService"
DyLoader loader= new DyLoader();
Class a loader.loadFromCustomRepository("com.lijz.SampleDyService"); 
DyLoader newLoader = new DyLoader();
Class b = newLoader.loadFromCustomRepository("com.lijz.SampleDyService");

言归正传,停止介绍Classloader,回到利用Classloader来避免重新启动你的应用程序

首先定义业务逻辑处理模块接口

public interface IDyService
{
   public void start();
   public void close();
   public void doBusiness();
}

start方法用于初始化,close用于清除此服务。doBusiness用来模拟处理业务

一个实现的例子如下:
public class SampleDyService implements IDyService
{
    public SampleDyService()
    {
    }
    public void doBusiness()
    {
        System.out.println("hello boy");
    }
    public void start()
    {
        System.out.println("Start SampleDyService:");
        System.out.println(SampleDyService.class.getClassLoader());
    }

    public void close()
    {
        System.out.println("close SampleDyService:");
    }


start方法 close方法仅打印出提示信息。doBuinsess输出"hello boy"。主程序将循环调用doBusiness方法

public class Main()
{     
     private IDyService service = null;
     public Main()
            throws Exception
    {
        DyLoader loader = new DyLoader();
        service = (IDyService) loader.loadFromCustomRepository(
                "com.gctech.service.test.dyloader.SampleDyService").newInstance();
        service.start();
        
        while (true)
        {

            service.doBusiness();
            Thread.sleep(1000 * 3);
        }
    }
    
    public static void main(String[] args)
            throws Exception
    {
        Main main = new Main();
    }
    
}    
  
  
  假设业务逻辑改变,要求SampleDyService的doBusiness打印出"hello girl"。新编译好的SampleDyService已经覆盖了原来的类。在不启动应用程序前提条件下如何更新新的业务逻辑呢?
  分俩部来完成
  第一步,在Main类里添加notifyReLoad方法,用新的classloader重新生成SampleDyService实例
  public void notifyReLoad()
            throws Exception
    {
        service.close();
        DyLoader loader = new DyLoader();
        service = (IDyService) loader.loadFromCustomRepository(
                "com.gctech.service.test.dyloader.SampleDyService").newInstance();
        service.start();

    }
  
  第二步:使用某种机制检测来检测SampleDyService.class已经改变,如可以通过上面的例子启动一个线程检测SampleDyService.class是否被改变,如果改变则调用Main.notifyReLoad().也可以采用主动通知方式,如web应用中,提供这样的界面调用。在此例子中提供一个检测线程。
  public class DyServciceChecker extends Thread
{
    Main main = null;
    
    public DyServciceChecker()
    {
    }
    public void setMain(Main main)
    {
        this.main = main;

    }

    public void run()
    {
        while(!interrupted())
        {
            try
            {
                boolean isChanged = check();
                if(isChanged)
                {
                    main.notifyReLoad();

                        }
                else
                {
                    Thread.sleep(1000*50);


                }
            }
            catch (Exception ex)
            {
                ex.printStackTrace();
            }
        }
    }
       
}

修改Main类的构造函数成如下

 public Main()
            throws Exception
    {
        DyLoader loader = new DyLoader();
        service = (IDyService) loader.loadFromCustomRepository(
                "com.gctech.service.test.dyloader.SampleDyService").newInstance();
        service.start();
        //添加检测线程
        DyServciceChecker checker = new DyServciceChecker();
        checker.setMain(this);        
        checker.start();       
         
        while (true)
        {

            service.doBusiness();
            Thread.sleep(1000 * 3);
        }
    }
    
好了,运行Main类。并在运行过程中用新的SampleDyService.class覆盖旧的SampleDyService.class。控制台输出信息如下:


Start SampleDyService:

com.gctech.service.test.dyloader.DyLoader@108786b

hello boy

hello boy

hello boy

...............
close SampleDyService:

Start SampleDyService:

com.gctech.service.test.dyloader.DyLoader@c1cd1f

hello girl

hello girl



总结:
  如果应用程序不可避免的在运行中要重启动,你首先要做好的工作是采取合理的设计,保证用户的请求和对用户的相应不丢失。否则,只能等到用户访问量最少的时候去启动。还有你在写你的业务逻辑的时候,即使当时你很肯定你的代码写死也没关系也不要这么做,尽量采用配置文件的方法,并提供如上的检测线程,现在的web container都提供检测web.xml文件是否改变来确定是否需要重新部署web。最后Classloader能帮助你动态加载类,从而在不停止应用程序的情况下动态更新类




责任编辑:lss
  相关新闻
机考如何避免范围性错误
专家教您如何避免重启你的应用程序
Web应用中避免Form重复提交的三种方案
eclipse启动参数设置,加大内存使用率,避免出现内存不
编写多线程的Java应用程序-如何避免当前编程中最常见的
避免Java EE项目评估中的常见错误
如何避免Java程序的数据脏读问题?
避免Java EE项目评估中的常见错误
利用实体EJB来避免性能的缺陷
Web应用中避免Form重复提交的三种方案
避免Java EE项目评估中的常见错误
如何避免Java程序的数据脏读问题?
专家教您如何避免重启你的应用程序
编写多线程的Java应用程序-如何避免当前编程中最常见的
eclipse启动参数设置,加大内存使用率,避免出现内存不
利用实体EJB来避免性能的缺陷
避免Java EE项目评估中的常见错误
机考如何避免范围性错误
  评论
现在有100人对本文发表评论
查看所有评论
 
推  荐
 
100本成功必读热销书
热门招生
  北京文理研修学院   前进大学
  北京明园大学   北京建设大学
  北京邮电大学世纪学院   北方工商管理学院
  联想软件定向委培班   香港数码动画学院
  青年企业管理研修学院   北京华夏管理学院
热门培训
网络化办公专家培训认证 电子科技大学软件学院
软件测试工程师培训认证 北大青鸟十大授权培训
IT硬件工程师培训认证班 北京环球雅思荷兰预科
JAVA开发工程师培训 潜能时代IT服务管理培训
网络信息化工程师培训 清华大学继续教育学院
论坛精选
 
有些细节是男人也该注意的风度!最容易读错的字
某强人手机里保存的30条短信 中国十大高薪职业
最感人的十大韩剧经典台词 嫁给工程师的N个理由
爆强!只有一句话的鬼故事 转贴教你如何做妖精
 女人一定要記住的話 女人最好别嫁给最爱的男人
城市联盟
 大连 上海 天津 广州 西安 深圳  天津  青岛  大连  福州  沈阳  青海  连云港  南京  吉林  厦门  威海  辽宁  呼和浩特
Copyright © 2006   www.edu999.com   All rights reserved. 中国教育招生在线  版权所有
北京市通信管理局[2004]字第552号函    京ICP证040442号