背景

        有一个网友看了我的《Flink的classLoader加载机制(推测)-- 记一次程序问题中的探索》这篇文章,向我提问了一个问题,虽然这个问题我没有解决,但是我打算做一个小实验来验证一下解决思路的可行性。问题如下:

        

java 运行jar包远程debug java远程加载jar_URLClassLoader

        得知该朋友已经解决此问题,我只想到了前期思路,后续的class not found问题其实是jobmanager和taskmanager不在一个jvm中导致的,很厉害,学习了。他的文章:https://www.toutiao.com/i6883793897495986691/

实验

        因为我现在没有一套随便实验的flink集群,也没有阿里云的oss,所以就只好做个最简单的实验,证明我是可以从远程加载jar包然后使用jar包中的类的。

        首先,需要先创建一个maven工程,创建一个类叫做Remote,随便写一个可以打印东西的方法,然后把这个工程打成一个jar包。(当然,如果已经有一个jar包了,那就直接用也行)

        然后,在jar包所在的路径下,用python启动一个http服务,命令:  python -m SimpleHTTPServer 8888 

        最后用下面的代码就可以加载这个jar包并加载里面的类,然后执行类里的方法了。

public class Test {

    public static void main(String[] args) throws MalformedURLException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, InstantiationException {
        URLClassLoader loader = (URLClassLoader) Test.class.getClassLoader();
        // 获取本地jar文件的URL
//        File jarFile = new File("/Users/zgy/IdeaProjects/test/target/maven-thrift-client-0.0.1-SNAPSHOT.jar");
//        URL targetUrl = jarFile.toURI().toURL();
        // 获取远程jar文件的URL
        URL targetUrl = new URL("http://localhost:8888/target/maven-thrift-client-0.0.1-SNAPSHOT.jar");

        // 这个校验是为了避免重复加载的
        boolean isLoader = false;
        for (URL url : loader.getURLs()) {
            if (url.equals(targetUrl)) {
                isLoader = true;
                break;
            }
        }

        // 如果没有加载,通过反射获取URLClassLoader.allURL方法来加载jar包
        if (!isLoader) {
            Method add = URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{URL.class});
            add.setAccessible(true);
            add.invoke(loader, targetUrl);
        }

        // 加载指定的class,然后为其创建对象后执行其方法,这些操作都是用反射去做的
        Class<?> remoteClass = loader.loadClass("Remote");
        Object remoteInstance = remoteClass.newInstance();
        Method method = remoteClass.getDeclaredMethod("func");
        method.setAccessible(true);
        System.out.println(method.invoke(remoteInstance));
    }
}

        URLClassLoader既可以加载本地文件系统的jar包,也可以加载远程jar包。我猜测这些应该使用统一的io来实现的。

后记

        其实这个实验只是说明了URLClassLoader的能力和使用方法,网上有很多参考资料可以做到这个功能。但是这个方法还有一些缺陷,比如在我使用http协议加载远程jar包的时候,加载速度明显比本地要慢很多。这样的话,其实可以考虑加载之前先下载jar包,然后再加载。(如果下载jar包,是否可以直接下载到ext目录下,让jvm的extensionClassLoader去加载这个jar包呢?)

        另外,这个思路需要用反射去调用类和方法,使用起来很麻烦。我一度怀疑自己做的有问题,为啥jvm不需要这么复杂呢?但是仔细一想,jvm在编译期是需要对应的jar包在的,起码对应的接口要有;运行期也是需要有jar包在,加载后直接使用类去运行了。我们这里做的事情,有点像jvm在运行期加载jar包和找到jar包里的类的过程,而不仅仅是在外部调用类和执行类方法。

参考文章

(1)这篇文章代码很丰富,主题是如何加载jar包,后边的一些功能对于本文章来说不太需要看  

(2)最开始看的这篇文章,比较简单,但也胜在简单