`

Java项目热部署

 
阅读更多
类的热部署、卸载和替换

一、Java中classLoader的双亲委托机制(默认是system classLoader,也称为AppClassLoader,其双亲指的是Extend和BootTrap classLoader):
Java中ClassLoader的加载采用了双亲委托机制,采用双亲委托机制加载类的时候采用如下的几个步骤:
当前ClassLoader首先从自己已经加载的类中查询是否此类已经加载,如果已经加载则直接返回原来已经加载的类。
每个类加载器都有自己的加载缓存,当一个类被加载了以后就会放入缓存,等下次加载的时候就可以直接返回了。
当前classLoader的缓存中没有找到被加载的类的时候,委托父类加载器去加载,父类加载器采用同样的策略,首先查看自己的缓存,然后委托父类的父类去加载,一直到bootstrp ClassLoader.
当所有的父类加载器都没有加载的时候,再由当前的类加载器加载,并将其放入它自己的缓存中,以便下次有加载请求的时候直接返回。
还有一个特例是contextClassLoader,多用于框架中,相当于是前三种的后门。

二、 class的热替换(主要通过覆写ClassLoader中的loadClass方法,加入修改时间的判断,通过类的反射调用新类的方法实现热替换)
ClassLoader中重要的方法
loadClass
      ClassLoader.loadClass(...) 是ClassLoader的入口点。当一个类没有指明用什么加载器加载的时候,JVM默认采用AppClassLoader加载器加载没有加载过的class,调用的方法的入口就是loadClass(...)。如果一个class被自定义的ClassLoader加载,那么JVM也会调用这个自定义的ClassLoader.loadClass(...)方法来加载class内部引用的一些别的class文件。重载这个方法,能实现自定义加载class的方式,抛弃双亲委托机制,但是即使不采用双亲委托机制,比如java.lang包中的相关类还是不能自定义一个同名的类来代替,主要因为JVM解析、验证class的时候,会进行相关判断。

defineClass
      系统自带的ClassLoader,默认加载程序的是AppClassLoader,ClassLoader加载一个class,最终调用的是defineClass(...)方法,这时候就在想是否可以重复调用defineClass(...)方法加载同一个类(或者修改过),最后发现调用多次的话会有相关错误:
...
java.lang.LinkageError
attempted duplicate class definition
...
所以一个class被一个ClassLoader实例加载过的话,就不能再被这个ClassLoader实例再次加载(这里的加载指的是,调用了defileClass(...)放方法,重新加载字节码、解析、验证。)。而系统默认的AppClassLoader加载器,他们内部会缓存加载过的class,重新加载的话,就直接取缓存。所与对于热加载的话,只能重新创建一个ClassLoader,然后再去加载已经被加载过的class文件。

1. 测试时要修改内容的类对象
package testjvm.testclassloader;

public class Hot {
    public void hot() {
        System.out.println(" version 1 : " + this.getClass().getClassLoader());
    }
}
2. 覆写classLoad的UrlClassLoader类
package testjvm.testclassloader;
import java.io.File;
import java.io.FileNotFoundException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.HashMap;
import java.util.Map;
/**
* 主要功能是重新加载更改过的.class文件,达到热替换的作用 1
*/
public class HotSwapURLClassLoader extends URLClassLoader {
    // 缓存加载class文件的最后最新修改时间
    public static Map<String, Long> cacheLastModifyTimeMap = new HashMap<String, Long>();
    // 工程class类所在的路径
    public static String projectClassPath = "D:/workspace/JavaFunction/bin/";
    // 所有的测试的类都在同一个包下
    public static String packagePath = "testjvm/testclassloader/";

    private static HotSwapURLClassLoader hcl = new HotSwapURLClassLoader();

    public HotSwapURLClassLoader() {
        // 设置ClassLoader加载的路径
        super(getMyURLs());
    }

    public static HotSwapURLClassLoader getClassLoader() {
        return hcl;
    }

    private static URL[] getMyURLs() {
        URL url = null;
        try {
            url = new File(projectClassPath).toURI().toURL();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
        return new URL[] { url };
    }
    /**
     * 重写loadClass,不采用双亲委托机制("java."开头的类还是会由系统默认ClassLoader加载)
     */
    @Override
    public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        Class clazz = null;
        // 查看HotSwapURLClassLoader实例缓存下,是否已经加载过class
        // 不同的HotSwapURLClassLoader实例是不共享缓存的
        clazz = findLoadedClass(name);
        if (clazz != null) {
            if (resolve) {
                resolveClass(clazz);
            }
            // 如果class类被修改过,则重新加载
            if (isModify(name)) {
                hcl = new HotSwapURLClassLoader();
                clazz = customLoad(name, hcl);
            }
            return (clazz);
        }
        // 如果类的包名为"java."开始,则有系统默认加载器AppClassLoader加载
        // java.表示这是JDK原始的类,比如java.lang.String
        // 原始的loadClass包为: java.net;
        if (name.startsWith("java.")) {
            try {
                // 得到系统默认的加载cl,即AppClassLoader
                ClassLoader system = ClassLoader.getSystemClassLoader();
                clazz = system.loadClass(name);
                if (clazz != null) {
                    if (resolve)
                        resolveClass(clazz);
                    return (clazz);
                }
            } catch (ClassNotFoundException e) {
                // Ignore
            }
        }

        return customLoad(name, this);
    }
    public Class load(String name) throws Exception {
        return loadClass(name);
    }

    /**
     * 自定义加载
     *
     * @param name
     * @param cl
     * @return
     * @throws ClassNotFoundException
     */
    public Class customLoad(String name, ClassLoader cl) throws ClassNotFoundException {
        return customLoad(name, false, cl);
    }
    /**
     * 自定义加载
     *
     * @param name
     * @param resolve
     * @return
     * @throws ClassNotFoundException
     */
    public Class customLoad(String name, boolean resolve, ClassLoader cl) throws ClassNotFoundException {
        // findClass()调用的是URLClassLoader里面重载了ClassLoader的findClass()方法
        Class clazz = ((HotSwapURLClassLoader) cl).findClass(name);
        if (resolve)
            ((HotSwapURLClassLoader) cl).resolveClass(clazz);
        // 缓存加载class文件的最后修改时间
        long lastModifyTime = getClassLastModifyTime(name);
        cacheLastModifyTimeMap.put(name, lastModifyTime);
        return clazz;
    }
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false);
    }
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        // TODO Auto-generated method stub
        return super.findClass(name);
    }

    /**
     * @param name
     * @return .class文件最新的修改时间
     */
    private long getClassLastModifyTime(String name) {
        String path = getClassCompletePath(name);
        File file = new File(path);
        if (!file.exists()) {
            throw new RuntimeException(new FileNotFoundException(name));
        }
        return file.lastModified();
    }

    /**
     * 判断这个文件跟上次比是否修改过
     *
     * @param name
     * @return
     */
    private boolean isModify(String name) {
        long lastmodify = getClassLastModifyTime(name);
        long previousModifyTime = cacheLastModifyTimeMap.get(name);
        if (lastmodify > previousModifyTime) {
            return true;
        }
        return false;
    }

    /**
     * @param name
     * @return .class文件的完整路径 (e.g. E:/A.class)
     */
    private String getClassCompletePath(String name) {
        String simpleName = name.substring(name.lastIndexOf(".") + 1);
        return projectClassPath + packagePath + simpleName + ".class";
    }
}

3. 测试类:
package testjvm.testclassloader;
import java.lang.reflect.Method;
public class TestHotSwap {
    public static void main(String[] args) throws Exception {
        // 开启线程,如果class文件有修改,就热替换
        Thread t = new Thread(new MonitorHotSwap());
        t.start();
    }
}

class MonitorHotSwap implements Runnable {
    // Hot就是用于修改,用来测试热加载
    private String className = "testjvm.testclassloader.Hot";
    private Class hotClazz = null;
    private HotSwapURLClassLoader hotSwapCL = null;
    @Override
    public void run() {
        try {
            while (true) {
                initLoad();
                Object hot = hotClazz.newInstance();
                Method m = hotClazz.getMethod("hot");
                // 打印出相关信息
                m.invoke(hot, null);
                // 每隔10秒重新加载一次
                Thread.sleep(10000);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 加载class
     */
    void initLoad() throws Exception {
        hotSwapCL = HotSwapURLClassLoader.getClassLoader();
        // 如果Hot类被修改了,那么会重新加载,hotClass也会返回新的
        hotClazz = hotSwapCL.loadClass(className);
    }
}

测试运行时修改“version 1”到“version 2”,会看到动态输出的修改内容变成了“version 2”。

三、jar包的热部署


四、Tomcat下热部署
工具:JavaRebel



类的热部署:
http://www.xuehuile.com/blog/abc337f9515449959ab6cebc19d70361.html
http://my.oschina.net/zhaoxj/blog/140266
http://www.jiancool.com/article/84343280279/
http://wenku.baidu.com/view/1c5559f4ba0d4a7302763ab8.html
http://www.blogjava.net/heavensay/archive/2015/11/06/389685.html 类的热部署和卸载
http://blog.csdn.net/mycomputerxiaomei/article/details/24470465 动态加载jar文件
http://www.cnblogs.com/xwdreamer/archive/2011/12/05/2296918.html
http://huangqiqing123.iteye.com/blog/1461624
classLoader:
http://my.oschina.net/aminqiao/blog/262601
http://www.cnblogs.com/zhanjindong/p/3952445.html
http://calmness.iteye.com/blog/83978
分享到:
评论

相关推荐

    支持Java热部署的插件

    Java热部署技术是一种提高开发效率的重要工具,它允许开发者在运行时修改代码并立即生效,无需重新启动应用程序服务器。在传统的开发过程中,每次修改代码后都需要重新编译、打包,然后重启服务器才能看到更改的效果...

    java热部署工具

    Java热部署工具是一种在开发过程中无需重启应用服务器即可更新代码或资源的技术,极大地提高了开发效率。热部署使得开发者能够在运行时动态替换、添加或移除类,而不影响应用程序的正常运行。这种技术对于大型的、...

    实现Java热部署的几种解决方案.docx

    Java 热部署解决方案详解 Java 热部署是指在不停止应用程序的情况下,动态地更新和替换应用程序中的类、方法或资源。这种技术可以提高开发效率、缩短开发周期、降低维护成本。今天,我们将讨论实现 Java 热部署的几...

    rebu.zip springboot+mybatis+thymeleaf 热部署 java 热部署页面访问

    本项目“rebu.zip”显然整合了这三个框架,旨在创建一个完整的Java Web应用程序,同时支持热部署功能,以提升开发效率。 首先,让我们深入了解每个框架的核心特性: 1. **SpringBoot**:SpringBoot是由Pivotal团队...

    Java项目Windows环境一键部署_javawindows环境一键部署_

    然而,通过特定的技术和工具,我们可以实现Java项目的Windows环境一键部署,大大简化了运维工作。以下是一些关键知识点: 1. **Java可执行文件(.exe)生成**: - 使用工具如Launch4j或JSmooth,可以将Java应用...

    Maven项目中的热部署

    在IT行业中,Maven是一个广泛使用的构建自动化工具,主要用于Java项目的管理。它简化了构建过程,依赖管理和项目信息管理,使得开发团队能够更高效地工作。"Maven项目中的热部署"这一主题聚焦于如何在Maven管理的...

    Arthas基本使用_线上热部署_arthas项目在线诊断_

    综上所述,Arthas是Java开发者的得力助手,它提供的线上热部署和项目在线诊断功能大大简化了问题排查和性能优化的过程。通过深入学习和实践Arthas,开发者可以更高效地维护和优化Java应用程序。

    eclipse上如何进行热部署

    在eclipse中,热部署可以通过配置服务器和项目的部署方式来实现。下面将详细介绍eclipse热部署的配置步骤和相关知识点。 热部署的原理 热部署的原理是基于Java的类加载机制。当我们修改了Java类文件后,热部署机制...

    idea热部署工具.zip

    1. 安装并激活JRebel插件后,创建或打开一个Java项目。 2. 配置项目的运行配置,确保"Enable JRebel"选项被勾选。 3. 运行或调试项目,JRebel会自动监听代码变化。 4. 当修改代码并保存后,无需重启,JRebel会自动将...

    maven项目下用 jetty 启动热部署

    当我们使用Maven构建Java Web项目时,结合Jetty服务器,可以实现项目的热部署功能,即在开发环境中修改代码后无需重新启动服务器,改动就能实时生效,极大地提高了开发效率。 在Maven项目中启用Jetty热部署,主要...

    Intellij IDEA下建立maven管理的web项目,配置jetty热部署

    Intellij IDEA 下建立 Maven 管理的 Web 项目,配置 Jetty 热部署 在本文中,我们将学习如何使用 Intellij IDEA 建立一个基于 Maven 管理的 Web 项目,并配置 Jetty 热部署。 知识点 1:Intellij IDEA 中的 Maven ...

    eclipse修改代码后热部署(不需要重启服务

    因此,实际使用时需要根据项目需求和环境选择合适的热部署策略。 总之,通过以上介绍的Eclipse热部署方法,你可以更高效地开发Java Web应用程序,避免频繁的重启,提升开发效率。无论是使用JRebel插件,还是利用...

    Intellij IDEA 热部署处理方法(图解)

    2. IDEA 热部署实战:Java 项目热部署 在 Java 项目中,需要使用热部署工具包 spring-boot-devtools。按照上述步骤开启自动编译和自动 make,即可实现热部署。 三、IDEA 热部署注意项 1. 默认 settings 配置 在 ...

    jrebel_6.5.2_Java热部署插件

    **Java热部署插件——JRebel 6.5.2** JRebel是一款高效便捷的Java应用热部署工具,它的出现极大地提升了开发者的效率,避免了在开发过程中频繁重启应用服务器来查看代码更改的效果。标题提到的是"jrebel_6.5.2",这...

    myeclipse编译热部署配置

    在Java开发环境中,热部署允许开发者在不重启应用服务器的情况下修改代码并立即看到效果,极大地提升了开发效率。MyEclipse作为一款强大的Java EE集成开发环境,提供了对热部署的支持,但默认情况下并不开启。本篇...

    maven多模块项目+jetty热部署实例源码

    Jetty是一款轻量级的Java Web服务器和Servlet容器,支持热部署,即在不重启服务器的情况下更新Web应用。在开发过程中,热部署可以显著提高开发效率。 1. **集成Jetty到Maven** - 添加Jetty Maven插件到子模块的POM...

    Tomcat热部署,让你轻松部署

    MyEclipse作为一个强大的Java集成开发环境,支持与Tomcat的紧密集成,可以通过设置项目属性,选择“发布为热部署”,实现代码修改后自动部署到运行中的Tomcat实例。 总结,Tomcat的热部署功能极大地提升了开发效率...

    tomcat热部署+操作文档,修改任何java后台文件也不需要重启

    在IT行业中,Tomcat是一个广泛使用的Java应用服务器,主要用于运行基于Servlet和JSP的应用程序。"热部署"是开发者在开发过程中的一项重要功能,它...因此,对于快速迭代的项目来说,掌握和应用热部署技术是至关重要的。

    ecilipse+tomcat+jrebel实现热部署

    热部署是指在不重新编译和部署项目的情况下,实时更新项目的代码和配置的过程。 JRebel 是一个商业热部署工具,由 ZeroTurnaround 公司开发。JRebel 可以与 Eclipse 和 Tomcat集成,实现热部署的功能。热部署可以...

Global site tag (gtag.js) - Google Analytics