java中所有的类都是通过classloader加载的,classloader可以为java程序提供很好的动态特性,深入了解非常有必要。
bootstrap classloader(启动类加载器)在JVM运行的时候加载java核心的API以满足java程序最基本的需求,其中就包括用户定义的ClassLoader,这里所谓的用户定义是指通过java程序实现的ClassLoader,一个是ExtClassLoader,这个ClassLoader是用来加载java的扩展API的,也就是/lib/ext中的类,一个是AppClassLoader,这个ClassLoader是用来加载用户机器上CLASSPATH设置目录中的Class的,通常在没有指定ClassLoader的情况下,程序员自定义的类就由该ClassLoader进行加载。
当运行一个程序的时候,JVM启动,运行bootstrap classloader,该ClassLoader加载java核心API(ExtClassLoader和AppClassLoader也在此时被加载),然后调用ExtClassLoader加载扩展API,最后AppClassLoader加载CLASSPATH目录下定义的Class,这就是一个程序最基本的加载流程。
bootstrap classloader:加载java核心库和java程序最基本的需求。
ExtClassLoader:加载java的扩展API,/lib/ext中的类。
AppClassLoader:加载classpath目录设置的class。
1)classloader的分类
从上图看到classloader默认有3类,
BootstrapLoader
ExtClassLoader
AppClassLoader
同时你也可以定制自己的classloader
这些classloader分别加载不同路径下的类,路径如上图所示
2)父子关系的含义
如上图所示,这几个classloader存在父子关系
下层的classloader会委托上层的classloader加载某个类,如果上层找不到才轮到自己,也就是说下层classloader
能看到上层的类,但反过来不是这样,这也成为委托模型
3)父子关系是如何确定的?
默认情况下,BootstrapLoader,ExtClassLoader,AppClassLoader的父子关系是固定,如果你定义了自己的classloader,也许会有多级,那么自定义classloader的初始化的所在的类就默认成为parent
比如你会在AppClassLoader中定义类加载器,此时你定义的classloader的parent就是AppClassLoader
4)类与它所依赖的类的classloader机制
如果一个类是由某个classloader加载,那么这个类依赖的类(非显式的通过某个classloader加载)必须也由该classloader或其父classloader加载,无视子classloader
比如A类依赖B类,你在A类的某个方法中new B(); 不可能A类由 BootstrapLoader加载,而B 类由 AppClassLoader
或者一个A类实现了C 接口,不可能A类由 BootstrapLoader加载,而C接口由 AppClassLoader
5)位于父classloader中的类真的无法依赖位于子classloader中的类码?
通过显式的使用classloader!
5.1)通过URLClassLoader.load
比如在位于AppClassLoader中的Test类中的main方法成功依赖了位于子classloader中Word类
URL u = new URL("file:/tmp/");
URLClassLoader ucl = new URLClassLoader(new URL[] { u });
Class c = ucl.loadClass("org.toy.classloader.Word");
Assembly asm = (Assembly) c.newInstance();
c.start();
5.2)通过thread.getContextClassloader
thread.getContextClassloader默认返回AppClassLoader,除非你显式setContextClassloader
上面的用法不过优雅,让我们从jdk jdbc的源码中学点经验
看看java是如何加载jdbc driver的
java.sql.DriverManager.getConnection---->
java.sql.DriverManager.initialize---->
java.sql.DriverManager.loadInitialDrivers---->
java.security.AccessController.doPrivileged---->
java.sql.DriverService.run---->
sun.misc.Service.providers(java.sql.Driver.class)---->
sun.misc.Service$LazyIterator()---->
sun.misc.Service$LazyIterator.next---->
Class.forName(cn, true, loader).newInstance();
上面的loader就是通过 Thread.currentThread().getContextClassLoader()传递的,在加载之前,上面的next方法,通过classloader.getResources("META-INF/services/java.sql.Driver") 发现文件
比如
jar:file:/home/pwlazy/dev/jdk1.6.0_17/jre/lib/resources.jar!/META-INF/services/java.sql.Driver
jar:file:/home/pwlazy/.m2/repository/mysql/mysql-connector-java/5.1.10/mysql-connector-java-5.1.10.jar!/META-INF/services/java.sql.Driver
这两个jar包都含有这个文件,文件的内容就是驱动的类名
比如上面两个文件分别对应类名sun.jdbc.odbc.JdbcOdbcDriver和 com.mysql.jdbc.Driver
于是classloader开始加载驱动类,
但最后开始开始连接数据库的时候,
会通过下面的方法来验证调用者是否有权限加载驱动类。
private static Class getCallerClass(ClassLoader callerClassLoader,
String driverClassName) {
Class callerC = null;
try {
callerC = Class.forName(driverClassName, true, callerClassLoader);
}
catch (Exception ex) {
callerC = null; // being very careful
}
return callerC;
}
做个小实验:
如果把调用类放在ExtClassLoader下,那么com.mysql.jdbc.Driver最终是无法调用的,因为调用类处于ExtClassLoader,而com.mysql.jdbc.Driver处于AppClassLoader,所以验证无法通过
但sun.jdbc.odbc.JdbcOdbcDrive是可以,因为它是BootstrapLoader加载的
从上面的分析,我们可以嗅到一个有趣的模式,某个框架制定某个API,而这些api的实现是有其他供应商来提供,为了能让框架类(处于较高层次的classloader)使用api的实现(处于较低层次的classloader)
通过thread.getContextClassloader是来传递classloader(有时候需要thread.setContextClassloader设置好api实现的classloader),用此classloader.getResources找出所有的api实现的具体类名,再用classloader加载之,此时框架都不需要知道api的实现类的类名就能加载之,程序显示了良好的动态性和可扩展性。
总之classloader默认的委托模型使得处于上层的classloader中的类无法访问处于下层classloader中的类,这带来了安全性的同时也失去了灵活性,幸好 可以使用Thread.currentThread().getContextClassLoader()
分享到:
相关推荐
在Java虚拟机(JVM)中,类加载器(ClassLoader)是至关重要的组成部分,它负责查找和加载类的字节码文件。理解ClassLoader的工作机制对于深入掌握Java应用程序的运行至关重要。这里我们将详细讨论ClassLoader的运行...
《深入理解ClassLoader工作机制》 Java虚拟机(JVM)中的ClassLoader是负责加载类到内存中的核心组件。它不仅承担着将字节码转换为可执行对象的重任,还参与了类生命周期的各个阶段,包括加载、验证、准备、解析、...
在Java中,Classloader是加载类的关键组件,它负责查找、加载和初始化字节码文件。自定义Classloader允许开发者根据特定需求定制类的加载逻辑,例如加密类文件、隔离不同版本的库或者动态加载代码。本文将深入探讨...
在Java编程语言中,ClassLoader是一个至关重要的组成部分,它负责加载类到JVM(Java虚拟机)中,使得程序能够执行。本示例"ClassLoader小例子"将深入探讨这个概念,并通过一个具体的程序来演示其工作原理。下面我们...
Java ClassLoader是Java运行时系统的关键但经常被忽视的组件,负责在运行时查找和加载类文件。通过创建自定义ClassLoader,你可以定制JVM,使类文件的引入方式完全重新定义,这提供了很多实用和有趣的可能。这篇教程...
Java ClassLoader机制是Java虚拟机(JVM)中一个至关重要的组成部分,它的主要任务是将类的.class文件加载到JVM中,使得程序能够运行。ClassLoader不仅负责类的加载,还涉及类的验证、初始化等一系列过程。理解...
在Java编程语言中,`ClassLoader`是一个至关重要的组件,它负责加载类到JVM(Java虚拟机)中。本文将深入探讨`ClassLoader`的工作原理、加密解密应用程序以及如何防止类被反编译。 首先,让我们理解`ClassLoader`的...
在Java编程语言中,ClassLoader是一个至关重要的组成部分,它负责加载类到JVM(Java虚拟机)中。理解ClassLoader的工作原理以及如何定制它,对于深入学习Java的运行机制和进行高级应用开发具有重要意义。本篇文章将...
在Java世界中,类加载器(ClassLoader)是关键的组件之一,它负责将类的字节码文件(.class)从文件系统或网络中加载到Java虚拟机(JVM)中,使得程序能够运行。本篇文章将深入探讨ClassLoader的关系网络以及如何...
《ClassLoader详解》 Java应用程序的运行离不开类的加载,而ClassLoader正是这个过程的关键角色。它负责将类的字节码加载到Java虚拟机(JVM)中并转换为可执行的Java对象。深入理解ClassLoader的工作原理对于优化...
Java ClassLoader机制是Java运行时环境中的核心组件之一,它负责加载类到JVM(Java虚拟机)中,使得程序能够执行。理解ClassLoader的工作原理对于优化应用性能、处理类加载问题以及实现自定义加载器至关重要。 首先...
在Java编程语言中,ClassLoader是核心组件之一,它负责加载类到JVM(Java虚拟机)中执行。本文将深入探讨ClassLoader的工作原理和类加载机制,帮助开发者理解这个至关重要的概念。 1. 类加载机制概述 Java的类加载...
在Java编程语言中,ClassLoader是一个至关重要的组成部分,它负责加载类到JVM(Java虚拟机)中执行。这篇测试主要探讨了ClassLoader的工作原理及其在实际应用中的使用。通过阅读给出的博文链接,我们可以深入理解...
### Java ClassLoader与ClassPath详解 #### 一、概述 在Java编程中,类加载机制是十分关键的一个环节。类加载器(`ClassLoader`)负责将编译后的`.class`文件加载到Java虚拟机(JVM)中执行,而类路径(`ClassPath...
Java的类加载器(ClassLoader)体系结构是JVM(Java虚拟机)中至关重要的一部分,它负责将类的字节码转换为运行时的类实例。本文将深入探讨启动类加载器、扩展类加载器、系统类加载器以及用户自定义类加载器,同时还...
破解java加密的ClassLoader.java,在classloader植入破解代码