JavaWeb无热部署扩展

太久没有写点东西了,今天分享一下Java web中我们的一个简单动态加载jar包,无需热部署以及更新以前的class即可上线服务应用,Java的反射机制内容这里不做科普(下面基本无代码,仅提供思路,代码党绕行)。

环境:java8+tomcat(tomcat中的类加与javase的加载器不是一样的,暂不做考虑);

数据交互:webservice(json)

思路:我们都知道,普遍情况下JavaWeb正常运行都需要编译为class文件才能解释运行,很多时候系统更新都需要重启服务或者服务自动热部署,无论两种模式如何,都会使得session失效以及部分任务中断。

php之所以普及,不仅仅它是脚本语言无需编译,同时哪个模块需要更新只需要更新某个文件而不需要重启服务就可以做到无缝更新了。基于此优势,于是想到利用Java动态加载模式 + jsp脚本模式进行项目部署、更新,尽可能做到代码的安全。

当我们创建一个web应用后,如果class内部有逻辑错误则需要更新class,这时web应用就需要得到重启更新,但事实上,在生产环境中给你这样的机会是少之又少,除非你能把控住用户使用时间段。有时候会想如果写jsp脚本,又会显得代码水平很low(只要安全、快速、优雅,jsp也是不错的选择),所以每天等到深夜12点后才会中断系统进行更新。

其实只需要动态加载jar文件,并定时更新就行了:

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package cn.skyatom.dynamicloader;

import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import sun.misc.ClassLoaderUtil;

/**
 * 动态加载反射机制加载器
 *
 * @author ZWK
 */
public class Loader {

    /**
     * 全局jar包加载器
     */
    private static java.util.Map<String, URLClassLoader> globJarLoader = new java.util.HashMap<String, URLClassLoader>();

    /**
     * 获取所有的加载器
     * @return 
     */
    public static java.util.List<URLClassLoader> getAllClassLoader() {
        java.util.Iterator<URLClassLoader> lds = globJarLoader.values().iterator();
        java.util.List<URLClassLoader> list = new java.util.ArrayList<URLClassLoader>();
        while(lds.hasNext()){
            URLClassLoader cl = lds.next();
            list.add(cl);
        }
        return list;
    }

    /**
     * 执行带参数构造器的方法
     *
     * @param jarid jar包id
     * @param clazname 完整类名
     * @param methodname 执行方法
     * @param ctypes 构造器参数类型
     * @param cvalues 构造器参数值
     * @param types 参数类型
     * @param values 参数值
     * @return
     * @throws java.lang.Exception
     */
    public static Object excMethod(String jarid, String clazname, String methodname, Class<?>[] ctypes, Object[] cvalues, Class<?>[] types, Object[] values) throws Exception {
        URLClassLoader loader = globJarLoader.get(jarid);
        if (loader == null) {
            return null;
        }
        Class<?> myclaz = loader.loadClass(clazname);
        Object c = myclaz.getConstructor(ctypes).newInstance(cvalues);
        Method m = myclaz.getMethod(methodname, types);
        Object rs = m.invoke(c, values);
        c = null;
        return rs;
    }

    /**
     * 执行空构造器的方法
     *
     * @param jarid jar包id
     * @param clazname 完整类名
     * @param methodname 方法名
     * @param types 参数类型
     * @param values 参数值
     * @return
     */
    public static Object excMethod(String jarid, String clazname, String methodname, Class<?>[] types, Object[] values) throws Exception {
        URLClassLoader loader = globJarLoader.get(jarid);
        if (loader == null) {
            return null;
        }
        Class<?> myclaz = loader.loadClass(clazname);
        Object c = myclaz.newInstance();
        Method m = myclaz.getMethod(methodname, types);
        Object rs = m.invoke(c, values);
        c = null;
        return rs;
    }

    /**
     * 执行静态方法
     *
     * @param jarid jar包id
     * @param clazname 完整类名
     * @param methodname 方法名
     * @param types 参数类型
     * @param values 参数值
     * @return
     */
    public static Object excStaticMethod(String jarid, String clazname, String methodname, Class<?>[] types, Object[] values) throws Exception {
        URLClassLoader loader = globJarLoader.get(jarid);
        if (loader == null) {
            return null;
        }
        Class<?> myclaz = loader.loadClass(clazname);
        Method m = myclaz.getMethod(methodname, types);
        Object rs = m.invoke(null, values);
        return rs;
    }

    /**
     * 执行带参数构造器的方法
     *
     * @param obj 对象
     * @param methodname 执行方法
     * @param ctypes 构造器参数类型
     * @param cvalues 构造器参数值
     * @param types 参数类型
     * @param values 参数值
     * @return
     * @throws java.lang.Exception
     */
    public static Object excMethodByObject(BindClaz obj, String methodname, Class<?>[] ctypes, Object[] cvalues, Class<?>[] types, Object[] values) throws Exception {
        Class<?> myclaz = obj.cClaz;
        Object c = obj.cObject;
        Method m = myclaz.getMethod(methodname, types);
        Object rs = m.invoke(c, values);
        c = null;
        return rs;
    }

    /**
     * 执行空构造器的方法
     *
     * @param obj
     * @param methodname 方法名
     * @param types 参数类型
     * @param values 参数值
     * @return
     */
    public static Object excMethodByObject(BindClaz obj, String methodname, Class<?>[] types, Object[] values) throws Exception {
        Class<?> myclaz = obj.cClaz;
        Object c = obj.cObject;
        Method m = myclaz.getMethod(methodname, types);
        Object rs = m.invoke(c, values);
        c = null;
        return rs;
    }

    /**
     * 执行静态方法
     *
     * @param jarid jar包id
     * @param clazname 完整类名
     * @param methodname 方法名
     * @param types 参数类型
     * @param values 参数值
     * @return
     */
    public static Object excStaticMethodByObject(BindClaz obj, String methodname, Class<?>[] types, Object[] values) throws Exception {
        Class<?> myclaz = obj.cClaz;
        Method m = myclaz.getMethod(methodname, types);
        Object rs = m.invoke(null, values);
        return rs;
    }

    /**
     * 创建对象
     *
     * @param jarid jar包id
     * @param clazname 完整类名
     * @param ctypes 参数类型
     * @param cvalues 参数值
     * @return
     */
    public static BindClaz createObject(String jarid, String clazname, Class<?>[] ctypes, Object[] cvalues) throws Exception {
        URLClassLoader loader = globJarLoader.get(jarid);
        if (loader == null) {
            return null;
        }
        Class<?> myclaz = loader.loadClass(clazname);
        if (ctypes != null) {
            Object c = myclaz.getConstructor(ctypes).newInstance(cvalues);
            BindClaz bc = new BindClaz();
            bc.cClaz = myclaz;
            bc.cObject = c;
            return bc;
        } else {
            Object c = myclaz.newInstance();
            BindClaz bc = new BindClaz();
            bc.cClaz = myclaz;
            bc.cObject = c;
            return bc;
        }
    }

    /**
     * 释放所有jar包
     */
    public static void releaseAllJarPck() {
        if (globJarLoader == null) {
            return;
        }
        java.util.Iterator<String> jids = globJarLoader.keySet().iterator();
        while (jids.hasNext()) {
            String jid = jids.next();
            releaseJarPck(jid);
        }
        globJarLoader.clear();
    }

    /**
     * 释放包资源
     *
     * @param jarid
     */
    public static void releaseJarPck(String jarid) {
        if (globJarLoader.get(jarid) != null) {
//            ClassLoaderUtil.releaseLoader(globJarLoader.get(jarid));//释放加载器
            globJarLoader.remove(jarid);
        }
    }

    /**
     * 从web协议加载
     *
     * @param jarid
     * @param weburl
     * @throws Exception
     */
    public static void loadJarPckByWeb(String jarid, String weburl) throws Exception {
        if (globJarLoader.get(jarid) != null) {
            ClassLoaderUtil.releaseLoader(globJarLoader.get(jarid));//释放加载器
        }
        URL jarurl = new java.net.URL(weburl);
        URLClassLoader myClassLoader = new URLClassLoader(new URL[]{jarurl}, Thread.currentThread().getContextClassLoader());
        globJarLoader.put(jarid, myClassLoader);
    }

    /**
     * 加载jar包资源
     *
     * @param jarid jar包id
     * @param jarPath jar包文件路径
     * @throws Exception
     */
    public static void loadJarPck(String jarid, String jarPath) throws Exception {
        if (globJarLoader.get(jarid) != null) {
            ClassLoaderUtil.releaseLoader(globJarLoader.get(jarid));//释放加载器
        }
        URL jarurl = new java.io.File(jarPath).toURI().toURL();
        URLClassLoader myClassLoader = new URLClassLoader(new URL[]{jarurl}, Thread.currentThread().getContextClassLoader());
        globJarLoader.put(jarid, myClassLoader);
    }

    /**
     * 绑定创建的类
     */
    public static class BindClaz {

        public Object cObject = null;//反射创建的对象
        public Class<?> cClaz = null;//当前类
    }
}

一个简单的动态加载执行方法,然后再配套一个所需要的代码生成器,将会非常节约开发时间以及相应的运维效率:

最后生成的代码放进json文件(jsp映射)中,只需要填入对应的参数即可。

**对于web应用,我在应用中写了一个定时器,定时更新最新版本的jar扩展包,一旦有更新的逻辑业务,都不用重启应用,也无需负载(负载迟早要做,条件允许情况下)。

希望能给阅读到这里的同学一点点启发。

****多余的代码也不粘贴了,基于以上思路估计花个几个小时,你自己也能写一个大约ok的动态加载模块。 谁说Java不能像php一样敏捷开发,换一个思路一样能走通

原文地址:http://blog.51cto.com/zwkpc/2085812

时间: 2024-08-30 10:52:33

JavaWeb无热部署扩展的相关文章

什么是无热AWG模块?

在大容量主传输网络建设中,DWDM(光密集波分复用系统)由于其大容量.支持多业务.可扩充性好等优点成为长途干线传输网上的主体.而在DWDM系统建设中,部分的机架规划是无电源供应(如WDM-PON等).无热型AWG (Athermal Arrayed Waveguide Grating) 正是为了匹配这种实际现场使用需求而推出的一种新型纯无源器件. 目前国内外开发的DWDM技术主要有3种类型,它们分别基于阵列波导光栅(AWG-Arrayed Waveguide Grating)和介质膜滤光片(TF

class卸载、热替换和Tomcat的热部署的分析

一 class的热替换 ClassLoader中重要的方法 loadClassClassLoader.loadClass(...) 是ClassLoader的入口点.当一个类没有指明用什么加载器加载的时候,JVM默认采用AppClassLoader加载器加载没有加载过的class,调用的方法的入口就是loadClass(…).如果一个class被自定义的ClassLoader加载,那么JVM也会调用这个自定义的ClassLoader.loadClass(…)方法来加载class内部引用的一些别的

Quartz与Spring整合进行热部署的实现(一)

先来几张实现图 任务管理页 新建任务管理.目前实现叫简单的需求...若各位同学要实现复杂的设计...quartz都有提供强大的支持.小弟目前的需求做到这已经够用了. 接下来.我们如何实现quartz的热部署编码呢? 小弟是利用spring整合quzrtz实现的.但是会产生两个小问题. 我们先看看quartz如何与spring整合 <bean name="quartzScheduler" class="org.springframework.scheduling.quar

【转】class卸载、热替换和Tomcat的热部署的分析

这篇文章主要是分析Tomcat中关于热部署和JSP更新替换的原理,在此之前先介绍class的热替换和class的卸载的原理. 一 class的热替换ClassLoader中重要的方法 loadClass ClassLoader.loadClass(...) 是ClassLoader的入口点.当一个类没有指明用什么加载器加载的时候,JVM默认采用AppClassLoader加载器加载没有加载过的class,调用的方法的入口就是loadClass(...).如果一个class被自定义的ClassLo

apache-maven-3.3.9集成apache-tomcat-7.0.72实现热部署配置细节

1.开发环境.Maven.Tomcat安装不作描述,搜索引擎很多文章可供参考. 2.Tomcat配置文件修改 1)Tomcat管理权限配置 1.1)在tomcat安装目录下找到tomcat-users.xml文件,如$apache-tomcat-7.0.72\conf\tomcat-users.xml,修改或增加如下内容: <tomcat-users> <role rolename="manager"/> <role rolename="admi

springCloud--补充:热部署

添加依赖:pom.xml <dependency>     <groupId>org.springframework.boot</groupId>     <artifactId>spring-boot-devtools</artifactId>     <scope>runtime</scope> </dependency> 在eclipse下此时就可以了,但是在idea下却无效果,解决如下:pom.xml

SpringBoot-03:SpringBoot+Idea热部署

  ------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 所谓热部署,就是在项目启动中,修改class类中做的修改操作,无需重新启动项目,就可以变更,在网页展示中有直接变更 首先idea需要三步骤: 热部署对于idea比较特殊,jar包+properties/yml的配置还不够,还需要改idea的设置 一,jar包依赖: <!--热部署--> <dependency> <groupId>org.springframework.boot

spring-boot-devtools 实现热部署

1.devtools spring为开发者提供了一个名为spring-boot-devtools的模块来使Spring Boot应用支持热部署,提高开发者的开发效率,无需手动重启Spring Boot应用. 2.项目搭建 本文是采用IDEA搭建的Spring Boot应用,通过spring-boot-devtools配置,可以支持修改java文件会自动重启程序,一些资源无需触发重启,例如thymeleaf模板文件就可以实时编辑.默认情况下,更改/META-INF/maven,/META-INF/

Java 调式、热部署、JVM 背后的支持者 Java Agent

我们平时写 Java Agent 的机会确实不多,也可以说几乎用不着.但其实我们一直在用它,而且接触的机会非常多.下面这些技术都使用了 Java Agent 技术,看一下你就知道为什么了. -各个 Java IDE 的调试功能,例如 eclipse.IntelliJ : -热部署功能,例如 JRebel.XRebel. spring-loaded: -各种线上诊断工具,例如 Btrace.Greys,还有阿里的 Arthas: -各种性能分析工具,例如 Visual VM.JConsole 等: