如何动态加载/卸载/重载本地外部jar包到项目,并调用其中的方法获取返回值

IT 文章4年前 (2021)发布 小编
0 0 0

最近有个需求是这样的,第三方提供一个外部的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;
    }
}

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

ad

程序员导航

优网导航旗下整合全网优质开发资源,一站式IT编程学习与工具大全网站

3)打成jar包

使用idea将其打成jar包,如果你不知道如何打jar可以参考:
[neilian ids=3226]
我这里将其打成名为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包到项目,并调用其中的方法获取返回值

ad

AI 工具导航

优网导航旗下AI工具导航,精选全球千款优质 AI 工具集

三、进阶动态重新加载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 

动态加载jar工具类-目前只支持加载/重新加载单个jar

**/ 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 

Hcdm下推的jar业务实现

**/ @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包到项目,并调用其中的方法获取返回值

ad

免费在线工具导航

优网导航旗下整合全网优质免费、免注册的在线工具导航大全

© 版权声明

相关文章

暂无评论

暂无评论...