`
chenruieye
  • 浏览: 38067 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

Java热部署1

 
阅读更多

名词解释:所谓热部署,就是在应用正在运行的时候升级软件 ,却不需要重新启动应用。

对于Java 应用程序来说,热部署就是在运行时更新Java类文件。在基于Java的应用服务器 实现热部署的过程中,类装入器扮演着重要的角色。大多数基于Java的应用服务器,包括EJB服务器和Servlet容器,都支持热部署。类装入器不能重新装入一个已经装入的类,但只要使用一个新的类装入器实例,就可以将类再次装入一个正在运行的应用程序。

我们知道,现在大多数的web 服务器都支持热部署,而对于热部署的实现机制,网上讲的却不够完善,下面我们就tomcat的热部署实现机制,讲解一下它是如何实现的:

Tomcat的容器实现热部署使用了两种机制:

1.  Classloader重写,通过自定义classloader加载相应的jsp 编译后的class到JVM中。

2.  通过动态修改内存中的字节码,将修改过的class再次装载到JVM中。

 

二.              Classloader 实现jsp 的重新加载

Tomcat通过org.apache.jasper.servlet.JasperLoader实现了对jsp的加载,下面做个测试:

1. 新建一个web工程,并编写一个jsp页面,在jsp页面中输出该页面的classloader,<%System.out.print(this.getClass().getClassLoader());%>.

2.  启动web服务器,打开jsp页面,我们可以看到后台输出,该jsp的classloader是JasperLoader的一个实例。

3.  修改jsp,保存并刷新jsp页面,再次查看后台输出,此classloader实例已经不是刚才那个了,也就是说tomcat通过一个新的classloader再次装载了该jsp。

4.  其实,对于每个jsp页面tomcat都使用了一个独立的classloader来装载,每次修改完jsp后,tomcat都将使用一个新的classloader来装载它。

 

关 于如何使用自定义classloader来装载一个class这里就不说了,相信网上都能找到,JSP属于一次性消费,每次调用容器将创建一个新的实例, 属于用完就扔的那种,但是对于这种实现方式却很难用于其它情况下,如现在我们工程中很多都使用了单例,尤其是spring工程,在这种情况下使用新的 classloader来加载修改后的类是不现实的,单例类将在内存中产生多个实例,而且这种方式无法改变当前内存中已有实例的行为,当然,tomcat 也没通过该方式实现class文件的重新加载。

 

三.              通过代理修改内存中class 的字节码

Tomcat中的class文件是通过org.apache.catalina.loader. WebappClassLoader装载的,同样我们可以做个测试,测试过程与jsp测试类似,测试步骤就不说了,只说一下结果:

         在热部署的情况下,对于被该classloader 加载的class文件,它的classloader始终是同一个WebappClassLoader,除非容器重启了,相信做完这个实验 你 就不会再认为tomcat是使用一个新的classloader来加载修改过的class了,而且对于有状态的实例,之前该实例拥有的属性和状态都将保 存,并在下次执行时拥有了新的class的逻辑,这就是热部署的神秘之处(其实每个实例只是保存了该实例的状态属性,我们通过序列化对象就能看到对象中包 含的状态,最终的逻辑还是存在于class文件中)。

下面的class重定义是通过:java.lang.instrument实现的,具体可参考相关文档。

         下面我们看一下如何通过代理修改内存中的class字节码:

以下是一个简单的热部署代理实现类(代码比较粗糙,也没什么判断):

package  agent;

 

import  java.lang.instrument.ClassFileTransformer;

import  java.lang.instrument.Instrumentation;

import  java.util.Set;

import  java.util.Timer;

import  java.util.TreeSet;

 

 

public   class   HotAgent {

    protected   static   Set<String>  clsnames =new  TreeSet<String>();

   

    public   static   void   premain(String  agentArgs, Instrumentation  inst)  throws  Exception {

       ClassFileTransformer  transformer =new  ClassTransform(inst);

       inst.addTransformer(transformer);

       System.out .println("是否支持类的重定义:"+inst.isRedefineClassesSupported());

       Timer  timer=new   Timer();

      timer.schedule(new  ReloadTask(inst),2000,2000);

    }

}

 

package  agent;

 

import  java.lang.instrument.ClassFileTransformer;

import java.lang.instrument.IllegalClassFormatException;

import  java.lang.instrument.Instrumentation;

import  java.security.ProtectionDomain;

 

public   class   ClassTransform.  implements  ClassFileTransformer {

    private   Instrumentation  inst;

    protected   ClassTransform(Instrumentation  inst){

       this .inst=inst;

    }

    /**

     * 此方法在redefineClasses时或者初次加载时会被调用,也就是说在class被再次加载时会被调用,

     * 并且我们通过此方法可以动态修改class字节码,实现类似代理之类的功能,具体方法可使用ASM或者javasist,

     * 如果对字节码很熟悉的话可以直接修改字节码。

     */

    public   byte []  transform(ClassLoader  loader, String  className,

           Class<?>  classBeingRedefined, ProtectionDomain  protectionDomain,

           byte []  classfileBuffer)throws  IllegalClassFormatException {

       byte []  transformed = null ;

       HotAgent.clsnames .add(className);

        return   null ;

    }

}

 

package  agent;

 

import  java.io.InputStream;

import  java.lang.instrument.ClassDefinition;

import  java.lang.instrument.Instrumentation;

import  java.util.TimerTask;

 

public   class   ReloadTask  extends   TimerTask {

   

    private   Instrumentation  inst;

    protected   ReloadTask(Instrumentation  inst){

       this .inst=inst;

    }

   

    @Override

    public   void   run() {

       try {

           ClassDefinition[]  cd=new  ClassDefinition[1];

           Class[]  classes=inst.getAllLoadedClasses();

           for (Class  cls:classes){

          if (cls.getClassLoader()==null ||!cls.getClassLoader().getClass().getName().equals("sun.misc.Launcher$AppClassLoader"))

                  continue ;

              String  name=cls.getName().replaceAll("\\.","/");

             cd[0]=new  ClassDefinition(cls,loadClassBytes(cls,name+".class"));

              inst.redefineClasses(cd);

           }

          

       }catch (Exception ex){

           ex.printStackTrace();

       }

    }

   

    private   byte []  loadClassBytes(Class  cls,String  clsname) throws   Exception{

       System.out .println(clsname+":"+cls);

       InputStream  is=cls.getClassLoader().getSystemClassLoader ().getResourceAsStream(clsname);

       if (is==null )return   null ;

       byte []  bt=new   byte [is.available()];

       is.read(bt);

       is.close();

       return   bt;

    }

}

 

以上是基本实现代码,需要组件为:

1.  HotAgent(预加载)

2.  ClassTransform(在加载class的时候可以修改class的字节码),本例中没用到

3.  ReloadTask(class定时加载器,以上代码仅供参考)

4.  META-INF/MANIFEST.MF内容为:(参数一:支持class重定义;参数二:预加载类)

Can-Redefine-Classes: true

Premain-Class: agent.HotAgent

5.  将以上组件打包成jar文件(到此,组件已经完成,下面为编写测试类文件)。

6.  新建一个java工程,编写一个java逻辑类,并编写一个Test类,在该测试类中调用逻辑类的方法,下面看下测试类代码:

package  test.redefine;

 

public   class   Bean1 {

    public   void   test1(){

      System.out .println("============================");

    }

}

 

package  test.redefine;

 

public   class   Test {

    public   static   void   main(String[] args)throws   InterruptedException {

       Bean1  c1=new   Bean1();

       while (true ){

           c1.test1();

           Thread.sleep (5000);

       }

    }

}

 

运行测试类:

java –javaagent:agent.jar test.redefine.Test

在测试类中,我们使用了一个死循环,定时调用逻辑类的方法。我们可以修改Bean1中的方法实现,将在不同时间看到不同的输出结果,关于技术 细节也没什么好讲的了,相信大家都能明白。

分享到:
评论

相关推荐

    支持Java热部署的插件

    Java热部署技术是一种提高开发效率的重要工具,它允许开发者在运行时修改代码并立即生效,无需重新启动应用程序服务器。在传统的开发过程中,每次修改代码后都需要重新编译、打包,然后重启服务器才能看到更改的效果...

    java热部署工具

    1. **JRebel**: JRebel是一款商业的Java热部署工具,它可以直接在JVM中实现类的热更新,而无需重启服务器。开发者只需要在IDE中配置JRebel插件,并将项目指向JRebel代理的类加载器,就可以实时看到代码更改的效果。 ...

    实现Java热部署的几种解决方案.docx

    Java 热部署解决方案详解 Java 热部署是指在不停止应用程序的情况下,动态地更新和替换应用程序中的类、方法或资源。这种技术可以提高开发效率、缩短开发周期、降低维护成本。今天,我们将讨论实现 Java 热部署的几...

    java热部署

    java热部署

    java开发热部署

    有没有在为修改java代码 反复重启tomcat而烦恼 下载热部署一切不成问题。内容:1.热部署 2.分给myeclipse 1g内存

    rebu.zip springboot+mybatis+thymeleaf 热部署 java 热部署页面访问

    本项目“rebu.zip”显然整合了这三个框架,旨在创建一个完整的Java Web应用程序,同时支持热部署功能,以提升开发效率。 首先,让我们深入了解每个框架的核心特性: 1. **SpringBoot**:SpringBoot是由Pivotal团队...

    深入探索 Java 热部署

    Java 热部署技术是开发过程中的一大挑战,它旨在实现在不重启 Java 虚拟机(JVM)的情况下更新应用程序的部分代码。由于 JVM 默认仅在启动时加载类,一旦类加载,即使原始的 .class 文件发生变化,JVM 也不会自动...

    美团IDEA插件实现Java应用的热部署实践

    美团IDEA插件实现Java应用的热部署实践 热部署是指在应用程序运行时升级软件,而不需要重新启动应用。对于Java应用程序来说,热部署就是在运行时更新Java类文件,同时触发Spring以及其他常用第三方框架的一系列重新...

    Java类热替换(热部署)源码

    1. **JRebel**:这是一个商业工具,它可以实时更新类、资源和配置文件,而无需重新部署应用。JRebel通过代理ClassLoader实现这一功能,但其核心原理并不开源。 2. **JVM工具JIT编译器**:Java虚拟机(JVM)的Just-...

    JRebel eclipse 热部署

    JRebel eclipse 热部署

    关于java热部署知识.doc

    Java热部署技术主要指的是在Java应用运行过程中,无需重启服务就能更新代码并立即生效的能力。这对于开发阶段提高效率和减少停机时间至关重要。虽然标准的Java虚拟机(JVM)并不直接支持类的动态加载和替换,但是...

    jrebel_6.5.2_Java热部署插件

    **Java热部署插件——JRebel 6.5.2** JRebel是一款高效便捷的Java应用热部署工具,它的出现极大地提升了开发者的效率,避免了在开发过程中频繁重启应用服务器来查看代码更改的效果。标题提到的是"jrebel_6.5.2",这...

    JAVA类加载器分析--热部署的缺陷(有代码示例,及分析)

    这篇博文“JAVA类加载器分析--热部署的缺陷”探讨了Java类加载机制以及在热部署场景下可能遇到的问题。热部署允许开发者在不重启应用的情况下更新代码,提高开发效率,但同时也存在一些挑战。 首先,我们来理解类...

    jboss热部署配置

    在开发过程中,为了提高效率,我们通常希望在不中断应用服务的情况下更新部署的应用程序,这就是所谓的“热部署”配置。这篇博客将探讨如何在JBoss中实现热部署。 首先,理解热部署的基本原理是至关重要的。热部署...

    eclipse上如何进行热部署

    热部署的原理是基于Java的类加载机制。当我们修改了Java类文件后,热部署机制会重新加载该类文件,并将其应用于当前的应用程序中。这要求服务器支持热部署机制,例如Tomcat、JBoss等。 eclipse配置热部署 下面是...

    Java 热部署操作笔记。Spring Tool Suite

    Java 热部署操作笔记。Spring Tool Suite.网上能看到的资源多事mvn 的,再这个文档里面,很详细的介绍了.yml元素的写法。和注意事项。

    jboss 热部署必须jar

    【标题】"jboss 热部署必须jar" 指的是在JBoss应用服务器上进行热部署所需的关键Java档案(jar)。热部署允许开发者在不关闭或重启服务器的情况下更新应用,大大提高了开发效率和调试速度。对于JBoss而言,这个特定...

    Erlang 中的Module级别热部署

    ### Erlang中的Module级别热部署 #### 一、引言 Erlang 是一种专为构建高并发、容错性强的分布式系统而设计的编程语言。它的独特之处在于支持轻量级进程(也称为协程)和热部署能力。本文将深入探讨Erlang 中的 ...

    eclipse修改代码后热部署(不需要重启服务

    1. 安装JRebel插件:JRebel是一款流行的热部署工具,它可以直接在Eclipse中集成。通过访问JRebel官方网站,下载并安装插件。安装后,你需要获取一个激活码,然后在Eclipse中激活该插件。JRebel能实时更新类、资源和...

Global site tag (gtag.js) - Google Analytics