`
shallon
  • 浏览: 72442 次
  • 性别: Icon_minigender_2
  • 来自: 0
文章分类
社区版块
存档分类
最新评论

CLASSLOADER与类的依赖关系

阅读更多
起因:为了排查一个生产环境的问题,需要打印特殊的调试日志。能否做到通过配置,调试的类优先于原有类的载入,回退的时候修改配置即可?

一般的情况下:可以将 CLASSPATH设置成:/opt/lib/debug.jar:/opt/lib/c.jar的时候debug.jar的类优先于c.jar的载入,调试类生效和打印调试日志。CLASSPATH设置成:/opt/lib/c.jar则还原回正式的类。

在多层ClassLoader的情况下,调试类放在ClassPath路径中,c.jar放在子ClassLoader加载路径中,却出了“意外”。为了说明情况,做了一个测试项目。系统载入结构如下:


被载入类Container和Item,Item是Container的成员类

public class Container {
	private Item item=null;
	public Container(){
		println();
		this.item=new Item();
		
	}
	public void println(){
		System.out.println("Container:"+this.getClass().getClassLoader());
	}
}
public class Item {
public Item(){
	println();
}
public void println(){
	System.out.println("Item:"+this.getClass().getClassLoader());
}
}

载入类为StartServer,其关键的载入代码如下:
private void start() {
        try {
        	System.out.println("***************************");
            // Load up the bootstrap container
            final ClassLoader parent = findParentClassLoader();

            String libDirString = System.getProperty("load_dir");

            File libDir;
            if (libDirString != null) {
                libDir = new File(libDirString);
                if (!libDir.exists()) {
                    throw new NullPointerException("dir not exit");
                }
            }
            else {
            		throw new NullPointerException("dir not exit");
            }

            System.out.println("parent is:"+parent.getClass());
            ClassLoader loader = new MYClassLoader(parent, libDir);
            
            Thread.currentThread().setContextClassLoader(loader);
            Class containerClass = loader.loadClass(
                    "Container");
            containerClass.newInstance();
        }
        catch (Exception e) {
            e.printStackTrace();
        }
}



测试结果:

1、Class Item放在AppClassLoader载入,Class Container放在MYClassLoader载入 上述载入正常运行。
E:\javaproject\ws2007\tempProject\bin>java -Dload_dir=E:/javaproject/ws2007/tempProject -cp . StartServer
***************************
parent is:class sun.misc.Launcher$AppClassLoader
file:/E:/javaproject/ws2007/tempProject/c.jar
Container:MYClassLoader@61de33
Item:sun.misc.Launcher$AppClassLoader@19821f



测试结果:2、Class Container放在AppClassLoader载入,Class Item放在MYClassLoader载入  出现NoClassDefFoundError错误
E:\javaproject\ws2007\tempProject\bin>java -Dload_dir=E:/javaproject/ws2007/tempProject -cp . StartServer
***************************
parent is:class sun.misc.Launcher$AppClassLoader
file:/E:/javaproject/ws2007/tempProject/c.jar
Container:sun.misc.Launcher$AppClassLoader@19821f
Exception in thread "main" java.lang.NoClassDefFoundError: Item
        at Container.<init>(Container.java:6)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
        at java.lang.Class.newInstance0(Class.java:355)
        at java.lang.Class.newInstance(Class.java:308)
        at StartServer.start(StartServer.java:48)
        at StartServer.main(StartServer.java:18)

分析:
根据大名鼎鼎的类加载委托规则,ClassLoader 类使用委托模型来搜索类和资源。每个 ClassLoader 实例都有一个相关的父类加载器。需要查找类或资源时,ClassLoader 实例会在试图亲自查找类或资源之前,将搜索类或资源的任务委托给其父类加载器。虚拟机的内置类加载器(称为 "bootstrap class loader")本身没有父类加载器,但是可以将它用作 ClassLoader 实例的父类加载器。

由于子ClassLoader含有父ClassLoader的引用,并且可以将委托父ClassLoader搜索类和资源,反之则不行。于是在上述第二个测试中,MYClassLoader被显式加载Container,MYClassLoader委托AppClassLoader进行加载该类,AppClassLoader加载Container的时候发现无法加载Item类,其父ClassLoader是ExtClassLoader和Bootstrap ClassLoader当然也无法加载Item类,  因为Item仅出现在MYClassLoader的加载路径中,因而出错。

结论:

父ClassLoader与子ClassLoader加载的类必须有正确的依赖关系,子ClassLoader加载的类可以依赖饮用父ClassLoader加载的类,反之不行,如果需要将一个类移动上父ClassLoader加载则需要将该类依赖的所有类移到同一ClassLoader或者移动到更高层的ClassLoader。

另外,在测试过程中,中了-jar的招,以此纪念。一个可执行的 JAR 必须通过 menifest 文件的头引用它所需要的所有其他从属 JAR。如果使用了 -jar 选项,那么环境变量 CLASSPATH 和在命令行中指定的所有类路径都被 JVM 所忽略。 (http://www-128.ibm.com/developerworks/cn/java/j-jar/index.html)
分享到:
评论

相关推荐

    java查看sun包源码-land:Land是一个通过类加载器的简单Java:trade_mark:依赖隔离容器

    类加载会在上下级ClassLoader之间有委托关系,如: 是否允许在上级ClassLoader中查找类。 即是否 委托。 允许在上级ClassLoader查找哪些类/包。 即可以配置 委托 的粒度。 只允许在某级ClassLoader查找哪些类/包,会...

    Java发展史_&_Java9、10新特性

    Modularity提供了类似于OSGI框架的功能,模块之间存在相互的依赖关系,可以导出一个公共的API,并且隐藏实现的细节,Java提供该功能的主要的动机在于,减少内存的开销,在JVM启动的时候,至少会有30~60MB的内存加载...

    基于模板的代码生成器LKGenerator1.1.0_x86

    实现思路:加载实体类的class文件到classLoader中,读取实体类的属性和注解,将类名、属性字段名、注解三者与模版进行结合生成最终的代码。 1.配置 (1)配置模版路径,这个配置可以配置用于生成代码的velocity模版...

    基于模板的代码生成器LKGenerator1.1.0_x64

    实现思路:加载实体类的class文件到classLoader中,读取实体类的属性和注解,将类名、属性字段名、注解三者与模版进行结合生成最终的代码。 1.配置 (1)配置模版路径,这个配置可以配置用于生成代码的velocity模版...

    java 面试题 总结

    Static Nested Class是被声明为静态(static)的内部类,它可以不依赖于外部类实例被实例化。而通常的内部类需要在外部类实例化后才能实例化。 22、JSP中动态INCLUDE与静态INCLUDE的区别? 动态INCLUDE用jsp:...

    Spring.3.x企业应用开发实战(完整版).part2

    3.1.3 通过容器完成依赖关系的注入 3.2 相关Java基础知识 3.2.1 简单实例 3.2.2 类装载器ClassLoader 3.2.3 Java反射机制 3.3 资源访问利器 3.3.1 资源抽象接口 3.3.2 资源加载 3.4 BeanFactory和ApplicationContext...

    Spring3.x企业应用开发实战(完整版) part1

    3.1.3 通过容器完成依赖关系的注入 3.2 相关Java基础知识 3.2.1 简单实例 3.2.2 类装载器ClassLoader 3.2.3 Java反射机制 3.3 资源访问利器 3.3.1 资源抽象接口 3.3.2 资源加载 3.4 BeanFactory和ApplicationContext...

    超级有影响力霸气的Java面试题大全文档

    Static Nested Class是被声明为静态(static)的内部类,它可以不依赖于外部类实例被实例化。而通常的内部类需要在外部类实例化后才能实例化。 25、JSP中动态INCLUDE与静态INCLUDE的区别?  动态INCLUDE用jsp:...

    LV32.2020..ProjectBuilder:自动化测试和构建过程(WIP)

    LV32.2020.....依存关系 PPL.ClassLoader-HenrikDueholm- 安装 将所有依赖项PPL链接到主文件夹旁边的PPL文件夹中。 笔记 构建到存储库文件夹旁边的PPL文件夹。 在提交构建产品之前,从那里复制到 。 链接

    JAVA核心知识点整理(有效)

    25 JAVA8 与元数据.................................................................................................................................25 2.4. 垃圾回收与算法 .................................

Global site tag (gtag.js) - Google Analytics