行业资讯 2025年08月6日
0 收藏 0 点赞 644 浏览 5879 个字
摘要 :

文章目录 一、测试外部jar 1)接口代码: 2)实现类代码: 3)打成jar包 二、动态加载jar 1)创建动态加载jar的工具类 2)动态加载并调用jar中方法 3)测试结果 三……




  • 一、测试外部jar
    • 1)接口代码:
    • 2)实现类代码:
    • 3)打成jar包
  • 二、动态加载jar
    • 1)创建动态加载jar的工具类
    • 2)动态加载并调用jar中方法
    • 3)测试结果
  • 三、进阶动态重新加载jar
    • 一、修改LoadJarUtils
    • 二、使用实现重载
    • 三、测试

    最近有个需求是这样的,第三方提供一个外部的jar给我们,由于该jar包经常会更新,如果我们项目直接将其引入成为依赖,那么每次jar包更新我们都需要重新发版自己的项目,然后重启服务器,这样特别麻烦,因此我们决定将该jar存在我们的服务器硬盘的一个目录中,看上去与项目没有任何关系,如果该jar更新我们就将本地的jar也更新,然后重新动态加载即可,那么现在的核心问题就是如何动态地实现引入本地的外部jar包,下面潘老师写了个测试案例,具体实现如下。

    一、测试外部jar

    我们首先新建一个测试的外部jar,这里我就写了一个简单的java项目,里面就一个接口和一个实现类,实现类中就一个带参数和返回值的方法,如下:

    1)接口代码:

    package com.remote.service;
    
    public interface CheckService {
        String check(String plan);
    }
    

    2)实现类代码:

    package com.remote.service.impl;
    
    import com.remote.service.CheckService;
    
    public class CheckServiceImpl implements CheckService {
    
        @Override
        public String check(String plan) {
    
            System.out.println("check方法参数:"+plan);
            return "校验内容:"+plan;
        }
    }
    

    提示:注意这里的包路径,后面会用到,该方法就是一个简单的入参校验方法(省略了校验逻辑),然后给个返回值

    3)打成jar包

    使用idea将其打成jar包,如果你不知道如何打jar可以参考:

    IDEA中如何将Java项目达成SDK(jar包)供其他项目使用

    文章目录 一、打包SDK步骤 二、其他项目引用SDK步骤 三、如果是Maven项目 最近在公司开发底层基于ht […]

    我这里将其打成名为check-plan.jar,存放在D:\\myJars目录下.

    二、动态加载jar

    我这里是Web项目,针对java项目也同样适用,具体实现如下:

    1)创建动态加载jar的工具类

    package com.sf.hlcs.sps.util;
    
    import java.io.File;
    import java.lang.reflect.Method;
    import java.net.MalformedURLException;
    import java.net.URL;
    import java.net.URLClassLoader;
    
    public class LoadJarUtils {
    
        /**
         * 动态加载本地jar,参数是jar绝对物理路径
         */
        public static void loadJar(String jarPath) {
    
            URL url = null;
            try {
                // 加载本地jar
                File jarFile = new File(jarPath);
                url = jarFile.toURI().toURL();
            } catch (MalformedURLException e) {
                e.printStackTrace();
            }
    
            if (url != null) {
                // 从URLClassLoader类中获取类所在文件夹的方法,jar也可以认为是一个文件夹
                Method addURL = null;
                try {
                    addURL = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                }
    
                // 获取方法的访问权限以便写回
                boolean accessible = addURL.isAccessible();
                try {
                    addURL.setAccessible(true);
    
                    // 获取系统类加载器
                    URLClassLoader classLoader = (URLClassLoader)ClassLoader.getSystemClassLoader();
                    addURL.invoke(classLoader, url);
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    addURL.setAccessible(accessible);
                }
            }
        }
    }
    

    2)动态加载并调用jar中方法

    下面我们动态加载jar,使用反射创建实例并调用其中的方法,具体如下:

    package com.sf.hlcs.sps.service.impl;
    
    import com.sf.hlcs.sps.service.HcdmJarService;
    import com.sf.hlcs.sps.util.LoadJarUtils;
    import java.lang.reflect.Method;
    import java.util.List;
    import org.springframework.stereotype.Service;
    @Service
    public class TestJarServiceImpl implements TestJarService {
    
        @Override
        public void validPlanTest() {
            // jar 路径
            String jarPath = "D:\\\\myJars\\\\check-plan.jar";
            // 加载
            LoadJarUtils.loadJar(jarPath);
            // 类路径
            String classPath = "com.remote.service.impl.CheckServiceImpl";
            try {
                Class<?> clazz = Class.forName(classPath);
                // 创建实例
                Object checkService = clazz.getDeclaredConstructor().newInstance();
                // 获取check方法,方法参数为String类型
                Method check = checkService.getClass().getMethod("check", String.class);
                // 调用check方法,传入参数,强转返回值
                String result = (String)check.invoke(checkService, "滑槽口计划内容参数");
                System.out.println(result);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

    3)测试结果

    我们发送请求触发该service调用,发现测试结果如下:
    如何动态加载/卸载/重载本地外部jar包到项目,并调用其中的方法获取返回值

    三、进阶动态重新加载jar

    回到我们最开始说的业务,对于动态加载jar,一般我们只需要在项目启动时加载一次就可以了,不需要反复调用LoadJarUtils.loadJar方法,另外,如果jar更新的,我们应该重新动态加载一次,但是重新加载新版本的jar前在不重启服务器的情况下就必须要先卸载掉原先加载的jar,然后再加载新版本的jar,才能实现重新加载的效果,以下是我的测试案例实现。

    一、修改LoadJarUtils

    之前的LoadJarUtils是直接将整个jar加载到系统类加载器中,在其他地方可以直接使用Class.forName(\"类路径\")调用,是很方便的,但是我目前没有找到针对自己加载jar的卸载方法,因此只能另辟蹊径,重写了LoadJarUtils如下,不过目前该LoadJarUtils只支持在项目中从启动到停止只调用一次,且只能加载一个jar,重载也是卸载当前加载的jar,然后加载新的jar,一般使用时,也就是在项目启动时调用一次loadJar,初始化URLClassLaoder对象,然后自己程序实现监听该jar是否更新,更新的就调用reloadJar实现重载,因此使用时务必小心。修改后代码如下:

    package com.sf.hlcs.sps.util;
    
    import java.io.File;
    import java.net.MalformedURLException;
    import java.net.URL;
    import java.net.URLClassLoader;
    import sun.misc.ClassLoaderUtil;
    
    /**
     * @author panziye
     * @description <p>动态加载jar工具类-目前只支持加载/重新加载单个jar</p>
     **/
    public class LoadJarUtils {
    
        private static URLClassLoader urlClassLoader;
        private static String jarPath = "D:\\\\myJars\\\\check-plan.jar";
    
        /**
         * 静态代码块-项目一启动就加载默认jar
         */
        static {
            loadJar(jarPath);
        }
        /**
         * 动态加载本地jar,初始化类加载器
         */
        public static void loadJar(String jarPath) {
    
            URL url = null;
            try {
                // 加载本地jar
                File jarFile = new File(jarPath);
                // 如果不存在该文件
                if(!jarFile.exists()){
                    System.out.println("指定加载的jar不存在, jarPath:" + jarPath);
                    return;
                }
                url = jarFile.toURI().toURL();
            } catch (MalformedURLException e) {
                e.printStackTrace();
            }
    
            if(url != null){
                urlClassLoader = new URLClassLoader(new URL[]{url});
            }
        }
    
        /**
         * 重新加载jar
         * @param newJarPath
         */
        public static void reloadJar(String newJarPath){
            // 卸载当前jar
            unloadJar();
            // 重新加载新的jar
            loadJar(newJarPath);
        }
    
        /**
         * 卸载当前jar
         */
        public static void unloadJar(){
            if(urlClassLoader != null){
                // 卸载当前动态加载进来的jar
                ClassLoaderUtil.releaseLoader(urlClassLoader);
            }
        }
    
        /**
         * 获取当前的UrlClassLoader
         * @return
         */
        public static URLClassLoader getUrlClassLoader() {
            if (urlClassLoader == null){
                throw new RuntimeException("动态加载jar可能出现了异常,导致无法获取UrlClassLoader");
            }
            return urlClassLoader;
        }
    }
    

    二、使用实现重载

    我们在使用时方式会有所不同,我在测试service中修改如下:

    package com.sf.hlcs.sps.service.impl;
    
    import com.sf.hlcs.sps.service.HcdmJarService;
    import com.sf.hlcs.sps.util.LoadJarUtils;
    import java.lang.reflect.Method;
    import java.net.URLClassLoader;
    import java.util.List;
    import javax.annotation.PostConstruct;
    import org.springframework.stereotype.Service;
    
    /**
     * @author panziye
     * @version 1.0
     * @date 2021-07-13 16:40
     * @description <p>Hcdm下推的jar业务实现</p>
     **/
    @Service
    public class TestJarServiceImpl implements TestJarService {
       
        /**
         演示重载jar
         这里只是为了演示,实际使用请和jar包更新同步
         */
        @Override
        public void reloadJar(){
            // jar 路径
            String jarPath = "D:\\\\myJars\\\\check-plan.jar";
            // 加载
            LoadJarUtils.reloadJar(jarPath);
        }
    
        /**
         * 演示调用,调用会稍微麻烦些
         */
        @Override
        public void validPlanTest() {
            // 需要加载的类的路径
            String classPath = "com.remote.service.impl.CheckServiceImpl";
            // 获取URLClassLoader
            URLClassLoader urlClassLoader = LoadJarUtils.getUrlClassLoader();
            try {
                // 加载类
                Class<?> clazz = urlClassLoader.loadClass(classPath);
                // 创建实例
                Object checkService = clazz.getDeclaredConstructor().newInstance();
                // 获取check方法,方法参数为String类型
                Method check = checkService.getClass().getMethod("check", String.class);
                // 调用check方法,传入参数,强转返回值
                String result = (String)check.invoke(checkService, "滑槽口计划内容参数");
                System.out.println(result);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

    三、测试

    首先我们启动web项目,会自动初次加载jar,然后触发validPlanTest,查看结果,再次修改check-plan.jar中内容,再次打包,更新jar,然后我们在不重启服务器的情况下,触发reloadJar方法,再次触发validPlanTest,发现输出内容发生了改变,确实实现了动态重载,测试结果如下;
    如何动态加载/卸载/重载本地外部jar包到项目,并调用其中的方法获取返回值

微信扫一扫

支付宝扫一扫

版权: 转载请注明出处:https://www.zuozi.net/8053.html

管理员

相关推荐
2025-08-06

文章目录 一、Reader 接口概述 1.1 什么是 Reader 接口? 1.2 Reader 与 InputStream 的区别 1.3 …

985
2025-08-06

文章目录 一、事件溯源 (一)核心概念 (二)Kafka与Golang的优势 (三)完整代码实现 二、命令…

463
2025-08-06

文章目录 一、证明GC期间执行native函数的线程仍在运行 二、native线程操作Java对象的影响及处理方…

347
2025-08-06

文章目录 一、事务基础概念 二、MyBatis事务管理机制 (一)JDBC原生事务管理(JdbcTransaction)…

455
2025-08-06

文章目录 一、SnowFlake算法核心原理 二、SnowFlake算法工作流程详解 三、SnowFlake算法的Java代码…

515
2025-08-06

文章目录 一、本地Jar包的加载操作 二、本地Class的加载方法 三、远程Jar包的加载方式 你知道Groo…

831
发表评论
暂无评论

还没有评论呢,快来抢沙发~

助力内容变现

将您的收入提升到一个新的水平

点击联系客服

在线时间:08:00-23:00

客服QQ

122325244

客服电话

400-888-8888

客服邮箱

122325244@qq.com

扫描二维码

关注微信客服号