Java运行时加载外部Jar包(class文件)
人民网>>社会·法治

Java运行时加载外部Jar包(class文件)

2025-06-24 12:11:53 | 来源:人民网
小字号

一、应用场景

我们在开发普通的Java业务系统时,都是正常的编译、打包、加载启动。但是在一些平台类、工具类的系统,需要支持外部插件式,以及轻量规则引擎动态加载外部业务规则等功能。

运行时加载外部Jar包(class文件),是其中一种技术解决方案,该方式广泛使用在各种主流开源组件,让开源社区其他开发者,扩展更多的功能或数据库等中间件对接。

二、URLClassLoader介绍

URLClassLoader可以从指定的URL路径中加载类资源,而不局限于传统的类路径,这些URL可以是文件系统路径、网络路径或者JAR文件路径等。这意味着可以从远程服务器或者动态生成的路径加载类文件,实现动态加载类,从而实现插件化或者动态扩展的功能。

import java.net.URL;import java.net.URLClassLoader;import java.net.MalformedURLException;public class Main {    public static void main(String[] args) throws MalformedURLException, ClassNotFoundException, IllegalAccessException, InstantiationException {        // 定义URL数组,指定要加载的类路径        URL[] urls = new URL[]{new URL("file:/path/to/classes/")};        // 创建URLClassLoader实例        URLClassLoader classLoader = new URLClassLoader(urls);        /**        使用jar包方式加载        URLClassLoader classLoader = new URLClassLoader(new URL[] { new File("path/to/your.jar").toURI().toURL() });        */                // 使用URLClassLoader加载指定类        Class clazz = classLoader.loadClass("com.example.MyClass");                // 实例化类对象        Object instance = clazz.newInstance();                // TODO: 使用加载的类对象进行操作    }}

可以看到,URLClassLoader每次初始化,都需要对jar包或class文件进行加载,这会非常消耗机器CPU性能,调用loadClass,同样会消耗CPU性能,因此在高并发的业务系统中,需要对ClassLoader以及加载后的class进行本地缓存。需要做好刷新、控制这个缓存,或做好版本的控制。

三、应用示例

以下代码截取自DataX源码(阿里巴巴开源的数据同步工具)

import com.alibaba.datax.common.exception.DataXException;import com.alibaba.datax.core.util.FrameworkErrorCode;import org.apache.commons.lang.StringUtils;import org.apache.commons.lang.Validate;import java.io.File;import java.io.FileFilter;import java.net.URL;import java.net.URLClassLoader;import java.util.ArrayList;import java.util.List;/** * 提供Jar隔离的加载机制,会把传入的路径、及其子路径、以及路径中的jar文件加入到class path。 */public class JarLoader extends URLClassLoader {    public JarLoader(String[] paths) {        this(paths, JarLoader.class.getClassLoader());    }    public JarLoader(String[] paths, ClassLoader parent) {        super(getURLs(paths), parent);    }    private static URL[] getURLs(String[] paths) {        Validate.isTrue(null != paths && 0 != paths.length,                "jar包路径不能为空.");        List dirs = new ArrayList();        for (String path : paths) {            dirs.add(path);            JarLoader.collectDirs(path, dirs);        }        List urls = new ArrayList();        for (String path : dirs) {            urls.addAll(doGetURLs(path));        }        return urls.toArray(new URL[0]);    }    private static void collectDirs(String path, List collector) {        if (null == path || StringUtils.isBlank(path)) {            return;        }        File current = new File(path);        if (!current.exists() || !current.isDirectory()) {            return;        }        for (File child : current.listFiles()) {            if (!child.isDirectory()) {                continue;            }            collector.add(child.getAbsolutePath());            collectDirs(child.getAbsolutePath(), collector);        }    }    private static List doGetURLs(final String path) {        Validate.isTrue(!StringUtils.isBlank(path), "jar包路径不能为空.");        File jarPath = new File(path);        Validate.isTrue(jarPath.exists() && jarPath.isDirectory(),                "jar包路径必须存在且为目录.");		/* set filter */        FileFilter jarFilter = new FileFilter() {            @Override            public boolean accept(File pathname) {                return pathname.getName().endsWith(".jar");            }        };		/* iterate all jar */        File[] allJars = new File(path).listFiles(jarFilter);        List jarURLs = new ArrayList(allJars.length);        for (int i = 0; i < allJars.length; i++) {            try {                jarURLs.add(allJars[i].toURI().toURL());            } catch (Exception e) {                throw DataXException.asDataXException(                        FrameworkErrorCode.PLUGIN_INIT_ERROR,                        "系统加载jar包出错", e);            }        }        return jarURLs;    }}

缓存JarLoader

/**     * jarLoader的缓存     */    private static Map jarLoaderCenter = new HashMap();    @SuppressWarnings("unchecked")    private static synchronized Class loadPluginClass throw Exception(            String pluginKey, String pluginClassName) {        JarLoader jarLoader = LoadUtil.getJarLoader(pluginKey);        try {            return (Class) jarLoader                    .loadClass(pluginClassName);        } catch (Exception e) {            throw e;        }    }    public static synchronized JarLoader getJarLoader(String pluginKey, String pluginPath) {        JarLoader jarLoader = jarLoaderCenter.get(pluginKey));        if (null == jarLoader) {            if (StringUtils.isBlank(pluginPath)) {                throw new RunningTimeException(String.format(                                "插件[%s]路径非法!",                                pluginKey));            }            jarLoader = new JarLoader(new String[]{pluginPath});            jarLoaderCenter.put(pluginKey, jarLoader);        }        return jarLoader;    }

(责编:人民网)

分享让更多人看到