类加载器ClassLoader之jar包隔离

小引子

最近做了一个根据同一模块的不同jar版本做同时测试的工具,感觉挺有意思,特此记录。

类加载器(ClassLoader)是啥?

把类加载阶段中的“通过一个类的全限定名(博主注:绝对路径)来获取描述此类的二进制字节流”这个动作放在Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类。实现这个动作的代码模块成为”类加载器“摘自周志明的《深入理解Java虚拟机》

ClassLoader的用途

  • 功能测试
    每个加载器,有自己的独立的类名称空间。比较两个类是否”相等“的前提是它们是由同一个类加载加载才有意义,即ClassLoader如果不同,两个类必定不等。这样使得在一个JVM中加载同一个模块的不同版本的jar成为现实,基于反射功能,我们同样可以很轻松实现不同版本的模块测试。本文后面会提供简单demo的实现。
  • 代码加密
    没有做过,想必是对class文件进行混淆、压缩、native等等手段后的解密过程,这类需求还没遇过。
  • OSGi
    是动态模型形同,在eclipse中插件的实现就是基于OSGi思想,而eclipse主要的应用就是插件,所以可以理解为eclipse插件是OSGi的应用典范。做的不多,仅限于了解。
  • 热部署
    不停止服务,动态替换目标文件。ClassLoader动态加载jar包,如果做一个工程化的东西可能会费些周章,但是原理并不复杂。
  • ...

总之,ClassLoader很重要,Java世界需要它。

功能测试小样

本人在本地生成了test1.jar和test2.jar两个jar包。这两个jar都有类com.array7.jvm.classloader.Target,此Demo要实现的是同时将这两个jar包的同名类加载到JVM并且各自执行。
** test1.jar Target.java **

package com.array7.jvm.classloader;

public class Target {
    public static void main(String[] args) {
        System.out.print("test1");
    }
}

** test2.jar Target.java **

package com.array7.jvm.classloader;

public class Target {
    public static void main(String[] args) {
        System.out.print("test2");
    }
}

** TestDriver**

public static void main(String[] args) throws MalformedURLException, ClassNotFoundException, IllegalArgumentException, SecurityException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        ClassLoader loader1 = new URLClassLoader(new URL[]{new URL("file:/home/liushijie/workspace/test/out/artifacts/test1/test1.jar")}, TestDriver.class.getClassLoader());
        ClassLoader loader2 = new URLClassLoader(new URL[]{new URL("file:/home/liushijie/workspace/test/out/artifacts/test2/test2.jar")}, TestDriver.class.getClassLoader());

        String className = "com.array7.jvm.classloader.Target";
        // loader1
        System.out.print("test1.jar \t");
        Class clazz1 = Class.forName(className, true, loader1);
        clazz1.getMethod("main", String[].class).invoke(null, (Object) null);

        System.out.println();

        // loader2
        System.out.print("test2.jar \t");
        Class clazz2 = Class.forName(className, true, loader2);
        clazz2.getMethod("main", String[].class).invoke(null, (Object) null);

        System.out.println();

        System.out.println("实例化后是否相等:" + clazz1.equals(clazz2));

    }

输出

test1.jar   test1
test2.jar   test2
实例化后是否相等:false

其他未提知识点

  • ClassLoader的层级关系
  • 双亲委托与打破
  • 自定义ClassLoader

其他参考资料

分类: Java


/**
 * 
 */
package com.codemacro.jcm.main;

import java.net.URL;
import java.net.URLClassLoader;

/**
 * @author HUANGLIAO322
 * @date 2018年9月10日
 *
 */
public class TestDriver {
	
	public static void main(String[] args) throws Exception {
//        ClassLoader loader1 = new URLClassLoader(new URL[]{new URL("file:/home/liushijie/workspace/test/out/artifacts/test1/test1.jar")}, TestDriver.class.getClassLoader());
//        ClassLoader loader2 = new URLClassLoader(new URL[]{new URL("file:/home/liushijie/workspace/test/out/artifacts/test2/test2.jar")}, TestDriver.class.getClassLoader());

		
		URL[] url1 = new URL[]{new URL("file:D:/Users/HUANGLIAO322/Desktop/bundle/jcm/lib/jcm.jar")};
		URL[] url2 = new URL[]{new URL("file:D:/Users/HUANGLIAO322/Desktop/bundle/jcm/lib/jcm1.jar")};
		ClassLoader loader1 = new URLClassLoader(url1, TestDriver.class.getClassLoader());
		ClassLoader loader2 = new URLClassLoader(url2, TestDriver.class.getClassLoader());
		
		String className = "com.codemacro.jcm.JCMMain";
        // loader1
		System.out.println("^^^^^^^^^^^^^^^^^^^^^^^^^^^");
		System.out.println("jcm.jar \t");
        Class clazz1 = Class.forName(className, true, loader1);
        clazz1.getMethod("main", String[].class).invoke(null, (Object) null);
        
        System.out.println(clazz1.getClassLoader());
        System.out.println();

        // loader2
        System.out.println("^^^^^^^^^^^^^^^^^^^^^^^^^^^");
        System.out.println("jcm1.jar \t");
        Class clazz2 = Class.forName(className, true, loader2);
        clazz2.getMethod("main", String[].class).invoke(null, (Object) null);
       
        System.out.println(clazz2.getClassLoader());

        System.out.println("实例化后是否相等:" + clazz1.equals(clazz2));
        
        System.out.println(clazz1.getClassLoader().getParent().equals(TestDriver.class.getClassLoader()));
        

    }

}

猜你喜欢

转载自blog.csdn.net/qq5132834/article/details/82594263