太久没有写点东西了,今天分享一下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.MapglobJarLoader = new java.util.HashMap (); /** * 获取所有的加载器 * @return */ public static java.util.List getAllClassLoader() { java.util.Iterator lds = globJarLoader.values().iterator(); java.util.List list = new java.util.ArrayList (); 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 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一样敏捷开发,换一个思路一样能走通