- 浏览: 39118 次
- 性别:
- 来自: 唐山
文章分类
最新评论
一. 概述
名词解释:所谓热部署,就是在应用正在运行的时候升级软件,却不需要重新启动应用。
对于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;
importjava.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的应用服务器实现热部署的过程中,类装入器扮演着重要的角色。大多数基于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;
importjava.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 并发实践
2012-03-23 11:02 0java并发编程实践笔记 1, 保证线程安全的三种方法: ... -
Map sort
2010-12-02 11:09 1812可能会遇到这样的情况,我可能要对Map<key,valu ... -
Java 解析 xml
2010-07-20 10:59 6941.介绍 1)DOM(JAXP Crimson解析器 ... -
java 编码
2010-07-01 22:06 657现代计算机采用的都是 ... -
Class文件格式解析
2010-07-01 21:40 889http://www.blogjava.net/javafan ... -
java中byte转换int时为何与0xff进行与运算
2010-07-01 21:29 908[转]java中byte转换int时为何与0xff进行与运算 ... -
java 的InputStream和OutputStream的理解
2010-06-21 15:10 1913(转)java 的InputStream和OutputStre ... -
Think in java
2010-04-19 13:26 1007一、第一章 对象导论 1、Alan Kay总结了面向对象的五 ... -
java-email(转)
2010-03-23 15:50 926最近在项目中用到发送 ... -
UNICODE与 UTF-8的转换详解
2010-03-19 10:18 4802转载请注明出处: http://www.ins1000.cn/ ... -
java中hashcode()和 equals()的详解[转]
2010-03-18 14:34 714java中hashcode()和 equals() ... -
细说java的util中的集合类
2010-03-03 14:54 728线性表,链表,哈希表是常用的数据结构,在进行Java开发时,J ... -
String 创建
2010-03-01 10:52 774package com; import org.junit.T ... -
java字符串连接
2010-02-01 17:22 1540注:引用http://www.blogjava.net/nok ... -
java
2010-01-06 16:53 643【转】JNDI的详细介绍(09-03-27) 收藏 看到这篇关 ...
相关推荐
热部署的核心原理是利用Java的类加载机制和字节码操作来实现。 首先,对于Java的类加载器(Classloader),其设计遵循“双亲委派模型”,但当涉及到热部署时,Tomcat使用了一种特殊的方式。在Tomcat中,每个Web应用...
在进行Maven工程的Tomcat热部署详解时,首先需要掌握Java后台开发中对于Tomcat热部署的基本知识。这涉及到Java环境的搭建,Tomcat的安装,以及Maven工程中对于热部署的配置方法。 Java环境的搭建是热部署的前提。在...
jreloader 是一款针对Tomcat的热部署工具,它可以监控应用中Java类文件的变化,并在检测到变化时自动重新加载,从而避免了每次修改代码后都需要手动重启服务器的繁琐步骤。这对于开发者来说,极大地提高了开发效率。...
#### 二、Tomcat热部署原理概述 热部署(Hot Deployment)是指在不重启服务器的情况下更新应用程序。Apache Tomcat支持一定程度上的热部署功能,主要通过检测类文件的变化并在运行时自动加载新版本的类来实现。然而...
这个版本是特别为热部署设计的,这意味着开发者可以在应用程序运行时无需重启Tomcat就能更新Web应用,极大地提高了开发效率。 首先,我们来看一下提供的压缩包文件的组成部分: 1. **LICENSE** 和 **NOTICE**:这...
**Tomcat服务器工作原理** Tomcat是一款开源的Java Servlet容器,它是Apache软件基金会下的Jakarta项目的核心组件,主要用于部署和运行Java Web应用程序。Tomcat以其轻量级、高效和易于管理的特性,成为了许多...
下面将详细介绍 JRebel 如何与 Tomcat 集成,实现项目修改后的热部署。 **1. JRebel 的工作原理** JRebel 是通过动态类加载技术,实现在运行时替换已经加载的类,以达到热部署的效果。它拦截了 JVM 的类加载过程,...
7. **部署与热部署**:Tomcat支持自动部署和热部署,只需将WAR文件放入webapps目录,Tomcat会自动解压并部署应用。修改应用后,无需重启服务器,Tomcat可以检测到变化并自动更新。 8. **安全性**:Tomcat提供多种...
首先,理解热部署的基本原理是至关重要的。热部署允许开发者在运行时更改或替换已部署的应用程序,而无需重启服务器。在JBoss中,这主要通过监视特定目录的变化来实现,当检测到新的或更新的类文件时,服务器会自动...
热部署的原理 热部署的原理是基于Java的类加载机制。当我们修改了Java类文件后,热部署机制会重新加载该类文件,并将其应用于当前的应用程序中。这要求服务器支持热部署机制,例如Tomcat、JBoss等。 eclipse配置热...
**Tomcat热部署** Tomcat作为一款流行的开源Java Servlet容器,内置了热部署功能。当Tomcat检测到Web应用程序的源代码或者类文件发生变化时,它会自动重新加载这些更改,用户无须手动重启服务器。这个特性是通过...
首先,我们需要理解Tomcat的工作原理。当一个Web应用被部署到Tomcat时,Tomcat会将应用的类加载到内存中。默认情况下,如果这些类被修改,Tomcat不会检测到变化,因此需要手动重启服务器来加载新的类。为了实现热...
通常,Java应用服务器如Tomcat、Jetty、JBoss等都提供了一定程度的热部署支持。以标题中的"jbos热部署"为例,JBoss是Red Hat公司的一个开源企业级应用服务器,它包含了对热部署的支持。 JBoss的热部署机制主要通过...
【热部署原理与实现】 在Java开发中,由于其编译执行的特点,相较于动态语言如PHP,进行快速迭代和调试通常需要重启应用服务器,这在Web应用开发中尤其不便。然而,通过使用像DCE VM(Dynamic Code Evolution ...
- **Tomcat服务器**:作为Servlet容器,Tomcat是执行JSP和Servlet的关键组件,能够将Java Web应用部署到网络上。 - **Sysdeo Eclipse Tomcat插件**:这个插件极大地简化了Eclipse与Tomcat的集成,允许用户在Eclipse...
2. **Tomcat配置**:Tomcat作为常用的Java Web服务器,其配置也会影响热部署。在`conf/server.xml`文件中,找到`<Host>`标签,添加或修改`reloadable="true"`属性,使得服务器能够自动检测应用的改动并重新加载。 3...
【描述】:Tomcat热加载Jrebel是通过Jrebel插件与Tomcat服务器的集成,实现Java应用的热部署。这意味着开发者在编码过程中可以实时看到代码更改的效果,无需每次都停止、重新启动服务器,极大地提高了开发迭代的速度...
4. **热部署原理** SpringLoaded通过在JVM层面监控类的加载,一旦发现有类被修改,它会卸载旧的类并加载新的版本,从而实现了热部署。 5. **使用限制** 虽然SpringLoaded提供了便利,但它并不适用于所有场景。...
本篇文章将详细探讨热部署的概念、原理以及如何在IntelliJ IDEA(简称idea)中实现热部署。 **热部署概念** 热部署,也被称为热更新或热替换,是指在应用程序运行时,能够自动检测到代码的变更并即时更新到运行中的...
为了解决这个问题,"热部署"技术应运而生,而"Eclipse热部署插件"就是专门用于实现这一目的的工具。 热部署(Hot Deployment)允许开发者在不关闭或重启应用服务器的情况下,实时更新应用程序的代码,大大提高了...