- 浏览: 491782 次
- 性别:
- 来自: 深圳
文章分类
- 全部博客 (234)
- aom学习 (7)
- 2007 (19)
- Java基础 (58)
- spring (7)
- linux (4)
- VMware (3)
- 网络 (3)
- 开发工具类 (14)
- webservices (2)
- 其他 (14)
- database (16)
- hibernate (8)
- jsp_html (14)
- jbpm (5)
- jquery (1)
- 办公软件 (6)
- struts2(webwork) (6)
- web服务器 (2)
- 项目管理 (7)
- 编程规范 (1)
- ps (2)
- JAVA工具类API (4)
- sun_java1.6 (6)
- 金融ISO8583 (1)
- SFTP (1)
- android (1)
- Linux_ftp_命令行下下载文件get与上传文件put的命令应用 (1)
- 公钥+私钥 (1)
- jvm (1)
最新评论
-
fyw2011_huawei:
有没有办法让谷歌也实现该功能? 因为在谷歌上调试时windo ...
window之createPopup方法 -
技术求:
好,flush 时才是execute
浅谈Hibernate的flush机制 -
xyzc2011:
言简意赅
公钥+私钥 -
smith789:
...
Windows 不能在 本地计算机 启动 OracleDBConsoleorcl -
zengshaotao:
肯定,因为要用到
jquery.js与自己的扩展的.js在导入时有先后顺序
超越reloadable=true, 在Tomcat运行时动态重载 收藏
为什么写这篇文档?
使用过hibernate, spring或其他大型组件,写过50个类以上的网络应用程序(web application)的开发者应该知道,当系统中有很多类时,如果开启了Tomcat的reloadable=true,那么每当相关文件改变时,Tomcat会停止web app并释放内存,然后重新加载web app.这实在是个浩大的工程。
所以我总是在想如果能有只重载某几个类的功能,将极大的满足我这个即时调试狂。
去年我在论坛上发帖,才发现已经有一些应用服务器具有了这个功能,比如WebLogic, WebSphere, 等等。好像还有一个很酷的名字,叫开发模式。看来我还是孤陋寡闻了点。
当然很多人都是在Tomcat上开发,包括我。我很喜欢它的轻小,那些大内存和高CPU消耗的应用服务器不愧为硬件杀手,没理由不改进Tomcat :)。
最终实现功能
我没有时间去研究Tomcat的文件监听机制,也没时间去把他写成”开发模式”这么完整的功能,我最终实现的是,实现重载功能的测试jsp--很抱歉我还是没办法写得更完整。当然,你可以在这个基础上进行改进。
阅读须知
阅读本文,你应该具备以下知识
jvm 规范有关类加载器的章节
http://java.sun.com/docs/books/vmspec/2nd-edition/html/VMSpecTOC.doc.html
Tomcat 类加载机制
http://www.huihoo.org/apache/tomcat/
java 反射机制
http://java.sun.com/docs/books/tutorial/reflect/
ant
(好象该网址被不定时封锁,有时能上,有时不能)
最好在你的电脑上安装ant,因为Tomcat源码包使用ant从互联网获得依赖包。不过我也是修改了一个错误才使它完全编译通过。
当然,你也可以用其他IDE工具检查并添加依赖包,在IDE中,其实你只需要添加jar直到使org.apache.catalina.loader.WebappClassLoader无错即可。
修改过程
说明
新添加的代码请添加到java文件的末尾,因为我在说明行数的时候,尽量符合原始行数
web app类加载器
在Tomcat中,org.apache.catalina.loader.WebappClassLoader是web app的类加载器,所以需要修改它实现重载功能。
资源列表
在WebappClassLoader中,有一个Map类型属性resourceEntries,它记载了web app中WEB-INF/classes目录下所加载的类,因此当我们需要重载一个类时,我们需要先将它在resourceEntries里删除,我编写了一个方法方便调用:
public boolean removeResourceEntry(String name) {
if (resourceEntries.containsKey(name)) {
resourceEntries.remove(name);
return true;
}
return false;
}
是否重载标志
让WebappClassLoader需要知道加载一个类是否使用重载的方式。所以我建立一个boolean 类型的属性和实现它的getter/setter方法:
private boolean isReload = false;
public boolean isReload() {
return isReload;
}
public void setReload(boolean isReload) {
this.isReload = isReload;
}
动态类加载器
根据jvm类加载器规范,一个类加载器对象只能加载一个类1次,所以重载实际上是创建出另一个类加载器对象来加载同一个类。当然,我们不需要再创建一个WebappClassLoader,他太大而且加载规则很复杂,不是我们想要的,所以我们创建一个简单的类加载器类org.apache.catalina.loader.DynamicClassLoader:
package org.apache.catalina.loader;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.CodeSource;
import java.util.*;
/**
* 动态类加载器
*
* @author peter
*
*/
public class DynamicClassLoader extends URLClassLoader {
/* 父类加载器 */
private ClassLoader parent = null;
/* 已加载类名列表 */
private List classNames = null;
/**
* 构造器
*
* @param parent
* 父类加载器,这里传入的是WebappClassLoader
*/
public DynamicClassLoader(ClassLoader parent) {
super(new URL[0]);
classNames = new ArrayList();
this.parent = parent;
}
/**
* 从类的二进制数据中加载类.
*
* @param name
* 类名
* @param classData
* 类的二进制数据
* @param codeSource
* 数据来源
* @return 成功加载的类
* @throws ClassNotFoundException
* 加载失败抛出未找到此类异常
*/
public Class loadClass(String name, byte[] classData, CodeSource codeSource) throws ClassNotFoundException {
if (classNames.contains(name)) {
// System.out.println("此类已存在,调用 loadClass 方法加载.");
return loadClass(name);
} else {
// System.out.println("新类, 记录到类名列表,并用类定义方法加载类");
classNames.add(name);
return defineClass(name, classData, 0, classData.length, codeSource);
}
}
/* *
* 重载此方法,当要加载的类不在类名列表中时,调用父类加载器方法加载.
* @see java.lang.ClassLoader#loadClass(java.lang.String)
*/
public Class loadClass(String name) throws ClassNotFoundException {
if (!classNames.contains(name)) {
//System.out.println("不在类名列表中,调用父类加载器方法加载");
return parent.loadClass(name);
}
return super.loadClass(name);
}
}
在webappClassLoader中添加DynamicClassLoader
添加属性
private DynamicClassLoader dynamicClassLoader = new DynamicClassLoader(this);
添加重建方法,以便需要再次重载时替换掉上次的类加载器对象
public void reCreateDynamicClassLoader() {
dynamicClassLoader = new DynamicClassLoader(this);
}
修改调用点
第832行,公开findClass方法
public Class findClass(String name) throws ClassNotFoundException {
第1569行,添加如下一行代码。
if (isReload) removeResourceEntry(name);
第1577行,这里好像是一个bug,具体原因我忘了-_-||
if ((entry == null) || (entry.binaryContent == null))
改为
if ((entry == null) || (entry.loadedClass == null && entry.binaryContent == null))
第1633~1636行
if (entry.loadedClass == null) {
clazz = defineClass(name, entry.binaryContent, 0, entry.binaryContent.length,
codeSource);
改为
byte[] classData = new byte[entry.binaryContent.length];
System.arraycopy(entry.binaryContent, 0, classData, 0,
classData.length);
if (entry.loadedClass == null) {
clazz = isReload ?
dynamicClassLoader.loadClass(name,
classData, codeSource) :
defineClass(name,
classData, 0, classData.length, codeSource);
测试代码
test.jsp
我测试用的jsp为$CATALINA_HOME/webapps/ROOT/test.jsp,由于webapp里面并不会显式加载tomcat的核心类,所以我们需要用反射代码调用WebappClassLoader的方法。代码如下:
<%
ClassLoader loader = (Thread.currentThread().getContextClassLoader());
Class clazz = loader.getClass();
java.lang.reflect.Method setReload = clazz.getMethod("setReload", new Class[]{boolean.class});
java.lang.reflect.Method reCreate = clazz.getMethod("reCreateDynamicClassLoader", null);
java.lang.reflect.Method findClass = clazz.getMethod("findClass", new Class[]{String.class});
reCreate.invoke(loader, null);
setReload.invoke(loader, new Object[]{true});
Class A = (Class)findClass.invoke(loader, new Object[]{"org.AClass"});
setReload.invoke(loader, new Object[]{false});
A.newInstance();
// 如果你使用下面这行代码,当重编译类时,请稍微修改一下调用它的jsp,让jsp也重新编译
//org.AClass a = (org.AClass)A.newInstance();
// 下面这些代码是测试当一个类不在DynamicClassLoader类名列表时的反应
//a.test();
//java.lang.reflect.Method test = a.getClass().getMethod("test", null);
//test.invoke(a, null);
%>
org.AClass
package org;
public class AClass {
public AClass() {
// 修改输出内容确认Tomcat重新加载了类
System.out.println("AClass v3");
}
public void createBClass() {
new BClass();
}
}
org.BClass
package org;
public class BClass {
public BClass() {
//修改输出内容确认Tomcat重新加载了类
System.out.println("BClass v1");
}
}
测试步骤
按照上述步骤修改Tomcat源码并编译。
用winzip/winrar/file-roller打开$CATALINA_HOME/server/lib/catalina.jar。把前面编译完成后的org.apache.catalina.loader目录下的class文件覆盖jar中同名文件。
编译org.AClass和org.BClass
启动Tomcat并在浏览器中打开测试页http://localhost:8080/test.jsp
修改org.AClass中的System.out.println();语句并重编译类。
按下F5按键刷新浏览器。
查看Tomcat控制台是否输出了不同的语句?
Good Luck! :)))
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/danning/archive/2006/04/14/663422.aspx
发表评论
-
myeclipse6.5反编译jad
2010-11-03 16:24 1373启动eclipse,打开:Window->Prefere ... -
推荐一款让桌面井然有序的软件
2010-07-14 11:36 1306Fences是著名的Stardock公司新推出的一款软件,用来 ... -
Apache vs Tomcat
2010-02-11 12:42 1184Apache vs Tomcat 本质区别:1)apac ... -
最使用且最好学的编程-------批处理
2010-02-10 23:07 981在入侵中,大家有过编一个简单的程序来实现你的目的,BAT应该是 ... -
为什么eclipse使用jre能编译java源文件
2010-02-10 22:06 1617Eclipse编译java原文件是用的自己的编译器(Eclip ... -
Eclipse javaTemplate
2010-01-27 15:22 1044eclipse->preferences->jav ... -
My Eclipse 弹出错误:Multiple operations have reported errors. Select an error to vie
2010-01-15 11:28 2616My Eclipse 弹出错误:Multiple operat ... -
Tomcat内存溢出的三种情况及解决办法分析
2009-10-30 15:25 969Tomcat内存溢出的原因 ... -
eclipse用javarebel动态编译
2009-08-14 09:23 2061jar包:javarebel.jar 随便放到硬 ... -
Tomcat 不再重启的工具
2009-08-14 09:21 3089===================== ... -
以空格代替Tab, 养成良好编码风格
2009-07-24 10:53 2623以空格代替Tab, 养成良好编码风格 在编写程序代码时用 ... -
MyEclipse 6.0.1经常出现这个错误
2009-07-16 14:56 1296An internal error occurred duri ... -
javarebel加快开发速度
2009-05-21 18:53 1180我们进行 java 开发和调试的时候,要反复重启 to ...
相关推荐
这里的`reloadable="true"`是实现自动发布的关键配置项,它告诉Tomcat在检测到应用有更新时自动重新加载应用。 ##### 3.2 配置`context.xml` 虽然在`server.xml`中已经指定了`reloadable="true"`, 但为了进一步...
这里,`path`属性定义了虚拟目录的访问路径,`docBase`指定了物理路径,而`reloadable="true"`则表示应用在运行时可以热更新,即无需重启服务器即可加载新的更改。 #### 步骤3:理解`<Context>`元素的其他关键属性 ...
- 修改`conf/server.xml`中的`Host`元素,添加`reloadable="true"`属性,让Tomcat监测应用的变化并自动重载。 - `META-INF/context.xml`中也可以添加`<Context>`元素,设置`reloadable="true"`,但这种方法只对...
在Java Web开发中,Tomcat是一个流行的开源Servlet容器,它允许开发者运行和部署Java Web应用程序。然而,每当代码发生修改,传统的方式是停止Tomcat,更新应用的WAR文件,然后重新启动服务器,这在开发阶段效率较低...
要开启它,你需要在`server.xml`中的`<Context>`元素中添加`reloadable="true"`属性。例如: ```xml <Context reloadable="true" privileged="true"> ``` `privileged="true"`属性是为了支持Invoker Servlet...
- Tomcat在开发模式下,可以通过设置`reloadable=true`在`conf/server.xml`中的`Context`元素内,启用类的热重载。但这仅适用于源代码改变后,重新编译并部署的 `.class` 文件。 4. **JMX(Java Management ...
在Java Web开发中,经常需要使用到JSP技术来构建动态网页。而在部署Web应用时,为了便于管理和访问资源,通常会使用虚拟目录的概念。虚拟目录可以简单理解为一个映射关系,它将服务器上的实际物理路径与URL中的路径...
`reloadable`属性控制是否在运行时重新加载类包,通常在开发阶段设为`true`,生产环境中设为`false`以提高性能。 3. **增加自定义Web部署文件** 这种方法推荐使用,无需重启Tomcat。在`$CATALINA_HOME/conf/...
- 操作:将`<Context>`标签修改为`<Context reloadable="true">`。这样,在开发阶段对Web应用的任何更改都将自动被Tomcat检测并重新加载,避免了重启服务器的麻烦。但在生产环境中,应谨慎使用此设置,以防性能影响...
在IT领域,特别是针对Web应用开发,JSP(JavaServer Pages)作为一种强大的技术,被广泛应用于构建动态网页。本文将基于给定的文件信息,深入探讨JSP开发环境的本地详细配置过程,以及如何确保配置的正确性,以便...
- `reloadable`:设置为`true`时,可以实现在不重启服务器的情况下自动加载项目更新。这对于开发阶段非常重要,可以大大节省时间。 - `docBase`:指定项目的文档基础目录,即项目文件的实际存放位置。 - `workDir...
<Context path="" docBase="D:\Java\tomcat_6_0_18_sdbx\webapps\ROOT" reloadable="true"/> ``` 这里将OpenCMS的应用基础路径设置为`ROOT`,并启用了自动重载功能。 3. **添加端口映射**:为了确保OpenCMS能够...