`

修改类不用重启tomcat的配置(非reloadable="true")

 
阅读更多
注意:自用

今天同事龙泉同学找到一个很提高效率的tomcat开发模式。该模式下修改类不用整体加载web app,开发效率提升很多。

经过我们分析,原有的tomcat reloadable="true" 方式,虽可加载变化过或新增的类但存在以下问题

1、内存清空,整体web app会重新加载,web.xml中配置的监听器会全部再执行。
2、其他没变的类也会重新加载。
3、当系统很大时,非常耗时。

这次方式会完全避免这些问题。

介绍下安装方式

1、在控制台输入 java -jar dcevm-0.2-win.jar
      (安装程序会替换掉java下 bin/client/jvm.dll 和 bin/server/jvm.dll ,并将以后的jvm.dll备份到相应目录下。还会将dcevm.jar 加到lib/ext/ 目录.)

2、eclipse.ini文件里面最后加入:
-vmargs
-Xms128M
-Xmx512M
-XX:PermSize=64M
-XX:MaxPermSize=512M
-XX:+PrintGCTimeStamps
-XX:+PrintGCDetails
-XX:+TraceClassLoading
-XX:+TraceClassUnloading
-XX:+PrintGCTimeStamps
-XX:+PrintGCDetails
-verbose:gc
-Xloggc:gc.log


3、要用tomcat debug方式启动才有用。

报错的这句话:Exception in thread "main" java.lang.NoSuchMethodError: org.objectweb.asm.ClassWriter. <init>(Z)V

问题原因

Hibernate3.3 Core Libraries 中的 cglib-2.2.jar 和 Spring 2.5 AOP Librariest 中的  cglib-nodep-2.1_3.jar 版本不一致 发生冲突。



解决方法

删除Hibernate3.3 Core Libraries 中的 cglib-2.2.jar 

操作步骤 为 windows->Preferences->MyEclipse->Project Capabilities ->Hibernate ->Hibernate 3.3

     Library modules 选择 Hibernate3.3 Core Library

     把cglib-2.2.jar  remove

     Project-->Clean 重新部署


分分分分分分分分分分分分分分分分分分分分分分分分分分分分分分
===================================
为什么写这篇文档?

使用过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

http://ant.apache.org/

(好象该网址被不定时封锁,有时能上,有时不能)

最好在你的电脑上安装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控制台是否输出了不同的语句?

============================
无项目名配置
<Context path="" docBase="E:/workspaces/ami/WebRoot" debug="0" ></Context>
有..
<Context path="/ami" docBase="E:/workspaces/ami/WebRoot" debug="0" ></Context>
分享到:
评论

相关推荐

    修改java类而无需重启tomcat.rar

    使用以下方法可以修改java类而无需重启tomcat: 下载javarebel.zip 解压后,将javarebel.jar拷贝到一个目录。/bin 目录下 修改tomcat安装目录下的\bin\catalina.bat文件, 增加set JAVA_OPTS=-noverify -javaagent:d...

    jsp+ExtJs+RMI 分布式宿舍管理系统 源码(内有数据库)

    tomcat 配置 &lt;Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true" xmlValidation="false" xmlNamespaceAware="false"&gt; &lt;Context path="/ourdorm-p1" reloadable="false" ...

    让用户分别使用apple和cloud虚拟目录访问Web服务目录.docx

    &lt;Context path = "/apple" docBase = "D:\MyBook\zhang" debug = "0" reloadable = "true"/&gt; &lt;Context path = "/cloud" docBase = "D:\wang" debug = "0" reloadable = "true"/&gt; 注意:xml文件是区分大小写的,不...

    修改Tomcat默认访问根目录的方法

    代码如下:&lt;Context path=”” docBase=”/usr/tomcat/apache-tomcat-8.5.11/webapps/guanwang” debug=”0″ reloadable=”true”/&gt; docBase即为默认访问的文件夹名称。Linux与Windows一样,只是文件夹地址有差别...

    Tomcat源码研究

    &lt;Context path="" docBase="ROOT" debug="0" reloadable="true"&gt;&lt;/Context&gt; &lt;Context path="/jsp/a" reloadable="true" docBase="E:\workplace\www.java2000. net\WebContent" /&gt;   使用默认配置的tomcat,...

    Tomcat-7.0.30

    把修改成&lt;Context reloadable="true"&gt; 修改目的:当Web应用中的文件或者web.xml文件修改后,Tomcat服务器 会自动重新加载当前Web应用,避免重新启动Tomcat。 这个修改会对Tomcat的运行性能有影响,如果把Tomcat...

    Tomcat6.0连接池配置

    &lt;Context path="/hrms" docBase="hrms" debug="5" reloadable="true" crossContext="true"&gt; 1. &lt;Resource name="jdbc/sql" 2. auth="Container" 3. type="javax.sql.DataSource" 4. driverClassName=...

    在tomcat6.0里配置虚拟路径

    在tomcat6.0里配置虚拟路径,&lt;Context path="/虚拟路径名" docBase="应用程序实际地址" reloadable="true" &gt; &lt;/Context&gt;

    tomcat 配置数据源

    debug="5" reloadable="true" crossContext="true"&gt; className="org.apache.catalina.logger.FileLogger" prefix="localhost_MysqlTest_log." suffix=".txt" timestamp="true"/&gt; ...

    在Eclipse 中配置Tomcat项目

    3.配置项目的Tomcat属性,在eclipse中右击项目preferences,选择Tomcat选项,把Is a Tomcat project打上勾,Can update context definition打上勾 mark this context as reloadable 打上勾,注意下面的Subdirectory ...

    apache-tomcat-7.0.21-embed.zip

    (1) 确认“JDK+Tomcat”的运行环境已经搭建好。 (2) 从光盘中将shopping目录拷贝到电脑中,这里假定拷贝...&lt;Context path="/shop" docBase="D:/shopping" debug="0" reloadable="true" crossContext="true"&gt; &lt;/Context&gt;

    tomcat-6.0.18去webapps

    tomcat-6.0.18.7z 去webapps和lib两个目录。 主要是用于比较其他的,看看虚拟目录...&lt;Context docBase="G:\\work\\flash\\flex\\EventDemo1\\bin-debug" reloadable="true" crossContext="true" debug="0"&gt; &lt;/Context&gt;

    详解tomcat热部署和热加载的方法

    详解tomcat热部署和热加载的方法 我在项目开发过程中,经常要改动Java/JSP 文件,但是又不想从新启动服务器(服务器从新启动花时间),想直接获得(debug)结果.有两种方式热部署 和热加载:  1.热加载:在server.xml...

    Tomcat实现热部署

    热部署是指在你对JSP或JAVA类进行了修改在不重启WEB服务器前提下能让修改生效,配置文件的修改除外 热部署好处 每次打增量包的时候就不用重新启动tomcat了 实现方式 在tomcat\conf\server.xml中的&lt;host&gt;&lt;/host&gt;内部...

    Tomcat数据源配置方法_JBuilder中

    其实tomcat中配置数据源有好几中方式,很灵活,所以也容易相互整窜,个人认为比较简单的方法就是在tomcat下的conf文件夹下的server.xml增加 代码如下: &lt;Context path=”/test” docBase=”test” xss=removed ...

    Apache2.2整合Tomcat6所用软件包

    Apache 配置 1. 配置httpd.conf 使用apache自带的proxy模块,去掉注释使其可用 LoadModule proxy_module modules/mod_proxy.so LoadModule proxy_ajp_module modules/mod_proxy_ajp.so LoadModule proxy_balancer...

    解决Tomcat在修改代码后不会自动reload的问题

    3.重启Tomcat,任意修改一个类文件内容,即会自动reload。 注意:如果是用Myeclipse,其中本身内置了一些各种版本的Tomcat 像这里也有Tomcat7的配置文件,之前网上有人说可以修改这里的内容,但是发布时用的是自己...

    tomcat自定义Web部署文件中docBase和workDir的区别介绍

    首先看这段tomcat配置文件: &lt;Context path=/web reloadable=false docBase=D:\CAPRuntime\src\main\webapp workDir=D:\CAPRuntime\src\main\webapp/&gt; 其中的docBase和workDir分别指的是什么意思呢? 让我们做一个...

    移动小商城:基于node,包含前后台.zip

    1.将my 文件夹,移动至 D盘根目录下 2.在eclipse 引入的tomcat 的Host 中加入 &lt;Context crossContext="true" ... &lt;Context crossContext="true" docBase="D:\my\ueditor\" path="/ueditor" reloadable="true"/&gt;

    java之路-项目搭建

    reloadable="true" 是否自动重启 好了,一切准备就绪 5.跑起来试下 记得要先清一下项目 好了,跑一下试试看吧 刚忘记说了,端口不能忘记,除非你用的是默认端口80 http://demo.cms.caiban.net:580/login.jsp 已经启好...

Global site tag (gtag.js) - Google Analytics