Tomcat热部署机制

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

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

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

Classloader重写,通过自定义classloader加载相应的jsp编译后的class到JVM中。 通过动态修改内存中的字节码,将修改过的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[feilcheng88.cn] 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(www.weicaiyule.cc);
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(http://www.aboguanwang.com/){
System.out.println("============================");
}
}

package test.redefine;

public class Test {
public static void main(String[http://blog.sina.com.cn/u/5922951005] args)throws InterruptedException {

Bean1 c1=new Bean1();
while(true){
c1.test1();
Thread.sleep(5000);
}
}
}
复制代码
运行测试类:

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

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

Tomcat 热部署配置

复制代码
<Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true" xmlNamespaceAware="false" xmlValidation="false">
<Context docBase="CPCWeb" path="/CPCWeb" http://yigouyule2019.cn/ reloadable="true" source="org.<span class="wp_keywordlink"><a href="http://res.importnew.com/eclipse" title="Eclipse ImportNew主页" target="_blank">Eclipse</a></span>.jst.j2ee.server:CPCWeb"/http://027yeshenghuowang.com/>
</Host>

复制代码
autoDeploy=”true” — 自动部署 reloadable=”true” — 自动加载

Tomcat热部署的实现原理的更多相关文章

  1. tomcat源码解读(1)–tomcat热部署实现原理

    tomcat的热部署实现原理:tomcat启动的时候会有启动一个线程每隔一段时间会去判断应用中加载的类是否发生变法(类总数的变化,类的修改),如果发生了变化就会把应用的启动的线程停止掉,清除引用,并且 ...

  2. Tomcat热部署,Web工程中线程没有终止

    近期项目中,用 jenkins 热部署 web工程时,发现工程中静态持有的线程(将ScheduledExecutorService定时任务存储在静态Map中),导致不定时出现数据库访问事务关闭异常,如 ...

  3. idea 开启 tomcat 热部署 的 具体流程 和 使用方式

    1前言 一直以来,使用idea做web开发修改html.jsp.js文件后,必须手动重新部署tomcat,最少都有等个6 -10 秒, 甚至有时候还提示找不到某个编译文件报错,重新编译整个项目,那得等 ...

  4. tomcat热部署,更改java类不用重新加载context

    修改类后,tomcat热部署会重新加载整个项目的context,影响开发效率.网上查的大多数是将server的modules标签中Auto Reload项改为Disabled,但是没有效果. 使用以下 ...

  5. IDEA第二章----配置git、tomcat(热部署)、database,让你的项目跑起来

    第一节:下载git客户端,整合idea 由于博主公司用的git版本管理,所以本系列都是基于git版本工具的,当然SVN与git配置类似.git同样支持安装版和解压版,支持各种操作系统,我这里下载的是W ...

  6. Tomcat 热部署

    Tomcat热部署就是实现不停机的发布项目和更新项目 1.修改conf目录下的tomcat-users.xml文件 在<tomcat-user>添加如下配置 [root@localhost ...

  7. tomcat热部署.class

    本人是在维护公司系统时遇到的问题,由于公司的系统是部署到客户服务器上,而系统中存在的问题又比较多,需要经常维护.如果每次修改完class文件后都需要去重启服务器, 那会给用户的使用造成不便,所以需要使 ...

  8. JAVAEE——宜立方商城14:项目部署规划、Tomcat热部署、反向代理的配置

    1. 学习计划 1.系统部署 2. 项目部署 2.1. 项目架构讲解 2.2. 网络拓扑图 2.3. 系统部署 2.3.1. 部署分析 e3-manager e3-manager-web e3-por ...

  9. 持续集成环境--Tomcat热部署导致线程泄漏

    一.问题由来 我们组用jenkins部署了持续集成环境,(jenkins部署war包到远程服务器的tomcat). 每次提交了代码,jenkins上一键构建,就可以自动拉取最新代码,打war包,热部署 ...

随机推荐

  1. ES doc_values介绍2——本质是field value的列存储,做聚合分析用,ES默认开启,会占用存储空间

    一.doc_values介绍 doc values是一个我们再三重复的重要话题了,你是否意识到一些东西呢? 搜索时,我们需要一个“词”到“文档”列表的映射 排序时,我们需要一个“文档”到“词“列表的映 ...

  2. JavaUtil_02_二维码的生成与解析

    1.引入jar包 zxing-core-1.7.jar  :   http://viralpatel.net/blogs/download/jar/zxing-core-1.7.jar zxing-j ...

  3. 使用JQuery,动态增加列

    这也是我在自己学做网站时无意搞出来的,希望可以对别人有所启发 <%@ page language="java" import="java.util.*" ...

  4. hdu-5637 Transform(位运算+bfs)

    题目链接: Transform Time Limit: 4000/2000 MS (Java/Others)     Memory Limit: 131072/131072 K (Java/Other ...

  5. Git 部署 Web 网站

    /*************************************************************************** * Git 部署 Web 网站 * 说明: * ...

  6. Agc011_F Train Service Planning

    先放题面,再放LHX巨佬题解 接着就是%%%.$orz.Oro.Or2.Otz.OTL.sto.rzo.Jto$.On_.○| ̄|_啊 模拟赛里直接把这道题刚掉了 一题升天·爆踩全场 这题思维跨越度已 ...

  7. JSTL1.0 知识回顾与总结

    JSTL1.0,由四个定制标志库(core,format,xml和sql) 和一对通用标记库验证器(ScriptFreeTLV 和 PermittedTaglibsTLV )组成. 1.基本输出设置操 ...

  8. [CERC 2008] Suffix reconstruction

    [题目链接] https://www.lydsy.com/JudgeOnline/problem.php?id=4319 [算法] 首先 , 我们可以求出这个字符串的rank数组 按照SA逐位枚举 , ...

  9. Android 开发:开源库Speex支持arm64的动态库文件

    随着处理器制造工艺的不断进步,和Android系统的不断发展,最近出了arm64-v8a的架构,由于项目中用到了speex的第三方语音编解码的动态库,其他架构的处理器暂不用说,一切正常,唯独到arm6 ...

  10. docker-建立私有registry

    我们知道可以使用hub.docker.com作为我们公共或者私有的registry.但由于服务器在国外的原因,网速会非常的慢.所以我们在利用docker开发构建容器服务时,我们希望能够建立自己的私有r ...