springboot自定义ClassLoader实现同一个jar支持多版本的使用场景【附源码】

springboot自定义ClassLoader实现同一个jar支持多版本的使用场景

  • 背景

    最近业务提出一个业务场景:系统目前支持hive3.1.0版本的数据源适配,但是有个别部门使用的数据源是hive2.1.1版本,但是hive3.1.0版本的驱动无法支持连接hive2.1.1的hive数据源;这就提出了新的目标:在同一个系统既要支持hive3.1.0版本同时又要支持hive2.1.1版本的数据源功能;

  • 思路分析

    1.java的执行都是通过类加载运行的,其加载运行过程如下:
    加载 >> 验证 >> 准备 >> 解析 >> 初始化 >> 使用 >> 卸载
    (不在详解,需要了解的百度一下吧),其多版本的兼容运行也离不开多版本的jar加载其运行:对于不同的hive版本分别classLoader不同的支持版本的lib jar就可以实现该目标;
    2.java类加载器有引导类(Launcher)、扩展类(ExtClassLoader)、应用程序类加载器(AppClassLoader)以及支持自定义加载器,其JVM类的加载类有一个双亲委派的机制:加载某个类时会先委托父加载器寻找目标类,找不到再委托上层父加载器加载,如果所有父加载器在自己的加载类路径下都找不到目标类,则在自己的类加载路径中查找并载入目标类。正是该机制导致了系统无法支持同一个jar的多版本加载;解决该问题就只能自定义加载类,打破双亲委派机制,通过指定的lib jar路径去加载需要class 以达到支持不同版本功能;

    经过如上的理论分析,实现方案有了理论支持,下面就直接开始代码实现上面的思路.

  • 代码实现和验证

    1.demo环境和前提准备:

    springboot 2.5.6
    mysql-connector-java 5.1.35
    hive 3.1.X需要的依赖包如:/Users/lifangyu/soft/driver-lib/hive-jdbc-3.1.2/
    hive2.1.1需要的依赖包路径如:/Users/lifangyu/soft/driver-lib/hive-jdbc-2.1.1/
    
    hive2.1.1 版本数据源信息
        url:jdbc:hive2://127.0.0.1:10001/demo
        user:test
        pwd:test
    hive3.1.X 版本数据源连接信息
        url:jdbc:hive2://127.0.0.1:2181/;serviceDiscoveryMode=zooKeeper;zooKeeperNamespace=hiveserver2
        user:test
        pwd:test
2.自定义加载类实现,打破双亲委派机制,通过自定义指定的jar 文件路径加载类JarClassLoader,源码如下:
package com.bigdata.myClassLoader;

import java.io.*;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * description 自定义加载类:打破双全委派机制
 *
 * @author Cyber
 * <p> Created By 2022/11/22
 * @version 1.0
 */
public class JarClassLoader extends URLClassLoader {

    private static ThreadLocal<URL[]> threadLocal = new ThreadLocal<>();
    private URL[] allUrl;

    public JarClassLoader(String[] paths) {
        this(paths, JarClassLoader.class.getClassLoader());
    }

    public JarClassLoader(String[] paths, ClassLoader parent) {
        super(getURLs(paths), parent);
        // 当前线程防止重复读取文件信息,可使用其他缓存代替
        allUrl = threadLocal.get();

    }

    public JarClassLoader(URL[] urls, ClassLoader parent) {
        super(urls, parent);
    }

    /**
     * description 通过文件目录获取目录下所有的jar全路径信息
     *
     * @param paths 文件路径
     * @return java.net.URL[]
     * @author Cyber
     * <p> Created by 2022/11/22
     */
    private static URL[] getURLs(String[] paths) {
        if (null == paths || 0 == paths.length) {
            throw new RuntimeException("jar包路径不能为空.");
        }
        List<String> dirs = new ArrayList<String>();
        for (String path : paths) {
            dirs.add(path);
            JarClassLoader.collectDirs(path, dirs);
        }
        List<URL> urls = new ArrayList<URL>();
        for (String path : dirs) {
            urls.addAll(doGetURLs(path));
        }
        URL[] threadLocalurls = urls.toArray(new URL[0]);
        threadLocal.set(threadLocalurls);
        return threadLocalurls;
    }

    /**
     
  • 4
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Spring Boot是一个用于构建Java企业级应用的开发框架,为开发者提供了自动配置、快速开发、便捷部署等功能。对于类加载器的使用Spring Boot提供了默认的类加载器机制,但也可以通过自定义类加载器来实现一些特殊的需求。 自定义类加载器可以通过继承ClassLoader类来实现。在自定义类加载器中,我们可以重写findClass方法,在该方法中实现自己的类加载逻辑。例如,可以从特定的位置加载类文件,或者从其他资源中加载类。通过自定义类加载器,我们可以灵活地加载一些不在常规位置的类文件或资源。 在Spring Boot中,可以使用自定义类加载器来实现一些插件化的功能。例如,可以实现一个插件类加载器,负责加载插件模块,并将其实例化为Spring Bean。这样,在运行时我们可以动态地加载并使用一些自定义的功能模块,而不需要在编译时就将其包含在应用程序中。 另外,自定义类加载器还可以用于热部署功能的实现。通过定时或者其他方式,我们可以在运行时重新加载某些类文件,以实现代码的热更新,避免了重启应用程序的操作。 需要注意的是,使用自定义类加载器需要谨慎。不正确的使用或者滥用类加载器可能会导致类冲突、内存泄漏等问题。因此,在自定义类加载器时,需要仔细考虑设计和实现,确保安全性和稳定性。 总之,Spring Boot提供了默认的类加载器机制,但也支持自定义类加载器。通过自定义类加载器,我们可以实现一些特殊的需求,例如插件化功能和热部署功能。但在使用自定义类加载器时,需要谨慎思考和设计,确保安全性和稳定性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值