有个同事提高个jrebel的工具,提起tomcat的热部署方案。

jrebel是一款收费的JVM级的热部署工具包。

JVM级的热部署也就是说,可以不重启JVM,让修改或添加的类加载到JVM中。

加载器:启动类加载器-》扩展类加载器-》应用程序类加载器-》自定义类加载器。

对JVM来说只有一种启动类加载器(Bootstrap ClassLoader)是C++写的。

这个类加载器负责将存放在<JAVA_HOME>\lib目录中的,

或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别的(仅按照文件名识别,如rt.jar,名字不符合的类库即使放在lib目录中也不会被加载)类库加载到虚拟机内存中。启动类加载器无法被Java程序直接引用。

Java实现的加载器的:扩展类加载器,应用程序类加载器,自定义类加载器。

什么是双亲委派模型?

按这个顺序:启动类加载器-》扩展类加载器-》应用程序类加载器-》自定义类加载器。去加载类。也就是当你用自定义类加载器加载某类时,一定要从启动类加载器(<JAVA_HOME>\lib)这个目录中的jar中找先。例如类java.lang.Object,它存放在rt.jar之中,无论哪一个类加载器要加载这个类,最终都是委派给处于模型最顶端的启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都是同一个类。

打破双亲委派模型

如果基础类又要调用回用户的代码,那该怎么办?这并非是不可能的事情,一个典型的例子便是JNDI服务,JNDI现在已经是Java的标准服务,它的代码由启动类加载器去加载(在JDK 1.3时放进去的rt.jar),但JNDI的目的就是对资源进行集中管理和查找,它需要调用由独立厂商实现并部署在应用程序的Class Path下的JNDI接口提供者(SPI,Service Provider Interface)的代码,但启动类加载器不可能“认识”这些代码,因为启动类加载器的搜索范围中找不到用户应用程序类,那该怎么办?为了解决这个问题,Java设计团队只好引入了一个不太优雅的设计:线程上下文类加载器(Thread Context ClassLoader)。这个类加载器可以通过java.lang.Thread类的setContextClassLoader()方法进行设置

Tomcat的类加载器架构

在Tomcat目录结构中,有3组目录(“/common/*”、“/server/*”和“/shared/*”)可以存放Java类库,另外还可以加上Web应用程序自身的目录“/WEB-INF/*”,一共4组,把Java类库放置在这些目录中的含义分别如下:

①放置在/common目录中:类库可被Tomcat和所有的Web应用程序共同使用。

②放置在/server目录中:类库可被Tomcat使用,对所有的Web应用程序都不可见。

③放置在/shared目录中:类库可被所有的Web应用程序共同使用,但对Tomcat自己不可见。

④放置在/WebApp/WEB-INF目录中:类库仅仅可以被此Web应用程序使用,对Tomcat和其他Web应用程序都不可见。

CommonClassLoader、CatalinaClassLoader、SharedClassLoader和WebappClassLoader则是Tomcat自己定义的类加载器,它们分别加载/common/*、/server/*、/shared/*和/WebApp/WEB-INF/*中的Java类库。其中WebApp类加载器和Jsp类加载器通常会存在多个实例,每一个Web应用程序对应一个WebApp类加载器,每一个JSP文件对应一个Jsp类加载器。Tomcat 6.x顺理成章地把/common、/server和/shared三个目录默认合并到一起变成一个/lib目录,这个目录里的类库相当于以前/common目录中类库的作用。

Tomcat和server.xml中的 reloadble=true时

<Context docBase="" path="" reloadable="true" source=""/></Host>

Tomcat的加载是怎样的呢?看看tomcaat源码。

public void backgroundProcess() {

if (reloadable && modified()) {

try {

Thread.currentThread().setContextClassLoader

(WebappLoader.class.getClassLoader());

if (context != null) {

context.reload();

}

} finally {

if (context != null && context.getLoader() != null) {

Thread.currentThread().setContextClassLoader

(context.getLoader().getClassLoader());

}

}

}

}

有个后台线程扫.class文件,如果有修改就使用(Appclassloader)应用程序类加载器去动态加载被修改过的class到JVM中:context.reload()重启上下文。也是就是重启当前的WEB应用程序;。而不是重启jvm去静态加载.class。

请看这个if:if (reloadable && modified()) ,如果reloadble=true而且有.class文件被修改过。就会重启上下文件。那么添加.class会不会也重启上下文呢?请看下面代码:

public boolean modified() {

if (log.isDebugEnabled())

log.debug("modified()");

for (Entry<String,ResourceEntry> entry : resourceEntries.entrySet()) {

long cachedLastModified = entry.getValue().lastModified;

long lastModified = resources.getClassLoaderResource(

entry.getKey()).getLastModified();

if (lastModified != cachedLastModified) {

if( log.isDebugEnabled() )

log.debug(sm.getString("webappClassLoader.resourceModified",

entry.getKey(),

new Date(cachedLastModified),

new Date(lastModified)));

return true;

}

}

// Check if JARs have been added or removed

WebResource[] jars = resources.listResources("/WEB-INF/lib");

// Filter out non-JAR resources

int jarCount = 0;

for (WebResource jar : jars) {

if (jar.getName().endsWith(".jar") && jar.isFile() && jar.canRead()) {

jarCount++;

Long recordedLastModified = jarModificationTimes.get(jar.getName());

if (recordedLastModified == null) {

// Jar has been added

log.info(sm.getString("webappClassLoader.jarsAdded",

resources.getContext().getName()));

return true;

}

if (recordedLastModified.longValue() != jar.getLastModified()) {

// Jar has been changed

log.info(sm.getString("webappClassLoader.jarsModified",

resources.getContext().getName()));

return true;

}

}

}

if (jarCount < jarModificationTimes.size()){

log.info(sm.getString("webappClassLoader.jarsRemoved",

resources.getContext().getName()));

return true;

}

// No classes have been modified

return false;

}

看到//jar has been added没有,所以肯定也会重启加载。

Jrebel热部署的原理就很清晰了,就是只加载调用appclassloader加载.class到jvm中,而不context.reload()重启上下文就行了。

浅谈jrebel的更多相关文章

  1. 浅谈 Fragment 生命周期

    版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Fragment 文中如有纰漏,欢迎大家留言指出. Fragment 是在 Android 3.0 中 ...

  2. 浅谈 LayoutInflater

    浅谈 LayoutInflater 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/View 文中如有纰漏,欢迎大家留言指出. 在 Android 的 ...

  3. 浅谈Java的throw与throws

    转载:http://blog.csdn.net/luoweifu/article/details/10721543 我进行了一些加工,不是本人原创但比原博主要更完善~ 浅谈Java异常 以前虽然知道一 ...

  4. 浅谈SQL注入风险 - 一个Login拿下Server

    前两天,带着学生们学习了简单的ASP.NET MVC,通过ADO.NET方式连接数据库,实现增删改查. 可能有一部分学生提前预习过,在我写登录SQL的时候,他们鄙视我说:“老师你这SQL有注入,随便都 ...

  5. 浅谈WebService的版本兼容性设计

    在现在大型的项目或者软件开发中,一般都会有很多种终端, PC端比如Winform.WebForm,移动端,比如各种Native客户端(iOS, Android, WP),Html5等,我们要满足以上所 ...

  6. 浅谈angular2+ionic2

    浅谈angular2+ionic2   前言: 不要用angular的语法去写angular2,有人说二者就像Java和JavaScript的区别.   1. 项目所用:angular2+ionic2 ...

  7. iOS开发之浅谈MVVM的架构设计与团队协作

    今天写这篇博客是想达到抛砖引玉的作用,想与大家交流一下思想,相互学习,博文中有不足之处还望大家批评指正.本篇博客的内容沿袭以往博客的风格,也是以干货为主,偶尔扯扯咸蛋(哈哈~不好好工作又开始发表博客啦 ...

  8. Linux特殊符号浅谈

    Linux特殊字符浅谈 我们经常跟键盘上面那些特殊符号比如(?.!.~...)打交道,其实在Linux有其独特的含义,大致可以分为三类:Linux特殊符号.通配符.正则表达式. Linux特殊符号又可 ...

  9. 浅谈Angular的 $q, defer, promise

    浅谈Angular的 $q, defer, promise 时间 2016-01-13 00:28:00  博客园-原创精华区 原文  http://www.cnblogs.com/big-snow/ ...

随机推荐

  1. 查看类属性和方法---structure

  2. Advanced WordCount

    Github:https://github.com/Hoyifei/SQ-T-Homework-WordCount-Advanced (注:Github上的所有代码由我代为提交) PSP:(注:部分实 ...

  3. c++ 类成员变量初始化总结

    最近在学习c++,不同类型的c++成员变量在初始化的时候也有很有的区别,查了一些资料之后再此记录一下: #include<iostream> using namespace std; // ...

  4. SQL SERVER 提供了一些时间函数:

    SQL SERVER 提供了一些时间函数:取当前时间:select getdate()取前一个月的时间:SELECT DATEADD(MONTH,-1,GETDATE()) 月份减一个月取年份:SEL ...

  5. Python lib库docker-py和docker的区别

    1)两者的安装方式 pip install docker A Python library for the Docker Engine API pip install docker-py A Pyth ...

  6. Info.plist文件配置及注意事项

    1.Info.plist文件配置 常见配置 2.注意事项 Info.plist文件移动路径修改编译报错:could not read data from '/Users/lelight/Desktop ...

  7. UIViewController函数调用顺序

    /*********** 0 执行1次而已 ******************/ + (void)load { NSLog(@" 0:%s", __func__); } /*** ...

  8. git rm 与 git reset

    https://www.cnblogs.com/sunshine-xin/articles/3521481.html 1. git rm --cached file will remove the f ...

  9. VIRT(虚拟内存)RES(常驻内存)和SHR(共享内存)

    VIRT: 1.进程“需要的”虚拟内存大小,包括进程使用的库.代码.数据,以及malloc.new分配的堆空间和分配的栈空间等: 2.假如进程新申请10MB的内存,但实际只使用了1MB,那么它会增长1 ...

  10. kuangbin专题十六 KMP&&扩展KMP HDU2609 How many (最小字符串表示法)

    Give you n ( n < 10000) necklaces ,the length of necklace will not large than 100,tell me How man ...