利用classloader实现jar包的加载和卸载,实现类的热替换

由于应用局限,一个classloader只加载一个jar包,所以代码为:

这是一个包装类:

public class MyClassLoader {
    @Resource
    private SystemConfig systemConfig;

    private final static ConcurrentHashMap<String,MyURLClassLoader> LOADER_CACHE = new ConcurrentHashMap<>();
    
//    private MyURLClassLoader urlClassLoader;


    public void loadJar(String jarName) throws MalformedURLException {
        MyURLClassLoader urlClassLoader = LOADER_CACHE.get(jarName);
        if(urlClassLoader!=null){
            return;
        }
        urlClassLoader = new MyURLClassLoader();
//        String path = systemConfig.getExternalClassPath();
        String path = "E:/testspace/externalClassPath";
        URL jarUrl = new URL("jar:file:/"+path+"/"+jarName+"!/");
        urlClassLoader.addURLFile(jarUrl);
        LOADER_CACHE.put(jarName,urlClassLoader);
    }
    

    public Class loadClass(String jarName,String name) throws ClassNotFoundException {
        MyURLClassLoader urlClassLoader = LOADER_CACHE.get(jarName);
        if(urlClassLoader==null){
            return null;
        }
        return urlClassLoader.loadClass(name);
    }

    public void unloadJarFile(String jarName) throws MalformedURLException {
        MyURLClassLoader urlClassLoader = LOADER_CACHE.get(jarName);
        if(urlClassLoader==null){
            return;
        }
//        String path = systemConfig.getExternalClassPath();
        String path = "E:/testspace/externalClassPath";
        String jarStr = "jar:file:/"+path+"/"+jarName+"!/";
        urlClassLoader.unloadJarFile(jarStr);
        urlClassLoader = null;
        LOADER_CACHE.remove(jarName);
    }
}

以下是真正运行的classloader:

public class MyURLClassLoader extends URLClassLoader {

    private JarURLConnection cachedJarFile = null;

    public MyURLClassLoader() {
        super(new URL[] {}, findParentClassLoader());
    }

    /**
     * 将指定的文件url添加到类加载器的classpath中去,并缓存jar connection,方便以后卸载jar
     * 一个可想类加载器的classpath中添加的文件url
     * @param
     */
    public void addURLFile(URL file) {
        try {
            // 打开并缓存文件url连接
            URLConnection uc = file.openConnection();
            if (uc instanceof JarURLConnection) {
                uc.setUseCaches(true);
                ((JarURLConnection) uc).getManifest();
                cachedJarFile = (JarURLConnection)uc;
            }
        } catch (Exception e) {
            System.err.println("Failed to cache plugin JAR file: " + file.toExternalForm());
        }
        addURL(file);
    }


    public void unloadJarFile(String url){
        JarURLConnection jarURLConnection = cachedJarFile;
        if(jarURLConnection==null){
            return;
        }
        try {
            System.err.println("Unloading plugin JAR file " + jarURLConnection.getJarFile().getName());
            jarURLConnection.getJarFile().close();
            jarURLConnection=null;
//            System.gc();
        } catch (Exception e) {
            System.err.println("Failed to unload JAR file\n"+e);
        }
    }


    /**
     * 定位基于当前上下文的父类加载器
     * @return 返回可用的父类加载器.
     */
    private static ClassLoader findParentClassLoader() {
        ClassLoader parent = MyURLClassLoader.class.getClassLoader();
        if (parent == null) {
            parent = MyURLClassLoader.class.getClassLoader();
        }
        if (parent == null) {
            parent = ClassLoader.getSystemClassLoader();
        }
        return parent;
    }


}

使用以上代码可以实现jar包、类的热替换,使用方法:

public class Test {
    public static void main(String[] args) {

        MyClassLoader classLoader = new MyClassLoader();
        while (true) {
            try {
                classLoader.loadJar("creatoo-demo-1.0-SNAPSHOT.jar");
                Class clz = classLoader.loadClass("creatoo-demo-1.0-SNAPSHOT.jar","com.creatoo.core.impl.CreatooServiceImpl");
                ParameterTranlate parameterTranlate = (ParameterTranlate) clz.newInstance();
                String input = parameterTranlate.getBookInput("sdf");
                System.out.println(input);
                Thread.sleep(1000);
                classLoader.unloadJarFile("creatoo-demo-1.0-SNAPSHOT.jar");
//                System.gc();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
Flink提供了动态加载jar包的功能,可以在运行时动态地添加和删除jar包,以便在不停止Flink作业的情况下更新代码。 具体实现步骤如下: 1. 创建一个插件目录,用于存放所有的可加载jar包。在Flink的配置文件flink-conf.yaml中添加以下配置: ``` plugin.dir: /path/to/plugin/dir ``` 2. 在Flink作业中使用Flink自带的ClassLoader加载插件目录中的jar包。可以使用以下代码实现: ``` final List<URL> jars = new ArrayList<>(); final File pluginDir = new File("/path/to/plugin/dir"); if (pluginDir.isDirectory()) { for (final File file : pluginDir.listFiles()) { if (file.getName().endsWith(".jar")) { jars.add(file.toURI().toURL()); } } } final URL[] urls = jars.toArray(new URL[0]); final ClassLoader pluginClassLoader = new URLClassLoader(urls, getClass().getClassLoader()); Thread.currentThread().setContextClassLoader(pluginClassLoader); ``` 3. 在代码中使用反射机制动态加载。例如,如果要加载一个名为"com.example.MyClass"的,可以使用以下代码实现: ``` final Class<?> clazz = pluginClassLoader.loadClass("com.example.MyClass"); final Object instance = clazz.newInstance(); ``` 4. 添加动态加载jar包到Flink作业中。可以使用以下命令实现: ``` ./bin/flink run -p 1 -c com.example.MyJob /path/to/MyJob.jar -Dflink.plugins.dir=/path/to/plugin/dir ``` 以上是实现动态加载jar包的基本步骤,具体实现可以根据自己的需求进行调整。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值