Java
中的所有类,必须被装载到jvm
中才能运行,这个装载工作是由jvm
中的类装载器完成的。JVM
在加载类的时候,都是通过ClassLoader
的loadClass()
方法来加载class的。public class TestClassLoader { public static void main(String[] args) { System.out.println(TestClassLoader.class.getClassLoader()); } }运行结果:
sun.misc.Launcher$AppClassLoader@4e0e2f2a
一、java ClassLoader体系
Java默认是有三个ClassLoader,按层次关系从上到下依次是:
- Bootstrap ClassLoader
- ExtClassLoader
- AppClassLoader
Bootstrap ClassLoader是最顶层的ClassLoader,比较特殊,是用C++编写集成在JVM中的,是JVM启动的时候用来加载一些核心类的,
比如:
rt.jar
,resources.jar
,charsets.jar
,jce.jar
等
import java.net.URL; public class TestClassLoader { public static void main(String[] args) { URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs(); for (int i = 0; i < urls.length; i++) { System.out.println(urls[i].toExternalForm()); } } }
运行结果为:
file:/C:/Program%20Files/Java/jre1.8.0_73/lib/resources.jar file:/C:/Program%20Files/Java/jre1.8.0_73/lib/rt.jar file:/C:/Program%20Files/Java/jre1.8.0_73/lib/sunrsasign.jar file:/C:/Program%20Files/Java/jre1.8.0_73/lib/jsse.jar file:/C:/Program%20Files/Java/jre1.8.0_73/lib/jce.jar file:/C:/Program%20Files/Java/jre1.8.0_73/lib/charsets.jar file:/C:/Program%20Files/Java/jre1.8.0_73/lib/jfr.jar file:/C:/Program%20Files/Java/jre1.8.0_73/classes
Bootstrap ClassLoader加载的类全都是java自有的核心类。
ExtClassLoader、AppClassLoader都是继承自Bootstrap ClassLoader。
ExtClassLoader是用来加载扩展类加载器,负责加载Java的扩展类库,默认加载JAVA_HOME/jre/lib/ext/目录下的所有的jar
AppClassLoader叫做系统类加载器,负责加载应用程序classpath目录下的所有jar和class文件,包括我们平时运行jar包指定cp参数下的jar包
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded Class<?> c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { //如果父加载器不为空,使用父加载器加载class if (parent != null) { c = parent.loadClass(name, false); } else { //如果父加载器也为空,使用bootstarpClassLoader加载 c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); //如果还为空,调用自定义的加载方法 c = findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; } }
这时问题是,parent是什么?
public abstract class ClassLoader { // The parent class loader for delegation private final ClassLoader parent; ... }
原来ClassLoader内部使用了父加载器,这就是双亲委托模式。
如图所示:
当前类加载器先不加载class,委托给父加载器加载class,如果父加载器没有加载成功,本类加载器再加载class。
可以使用下面代码测试下:
public class TestClassLoader { public static void main(String[] args) { ClassLoader loader = MyAppClassLoader.class.getClassLoader(); while(loader != null) { System.out.println(loader); loader = loader.getParent(); } System.out.println(loader); } }
运行结果为:
sun.misc.Launcher$AppClassLoader@4e0e2f2a sun.misc.Launcher$ExtClassLoader@2a139a55 null
java.lang.ClassCaseException
public class MyAppClassLoader extends ClassLoader{ @Override public Class<?> findClass(String name) throws ClassNotFoundException { //从特殊路径下读取class,或者从网络中读取class,不管咋样,实现自己的方法,返回加载class //... } }
四、ContextLoader当前classLoader
首先关于类的加载补充一点就是如果类A是被一个加载器加载的,那么类A中引用的B也是由这个加载器加载的(如果B还没有被加载的话),通常情况下就是类B必须在类A的classpath下。
那么问题来了。某些时候这种顺序机制会造成困扰,特别是jvm需要动态载入有开发者提供的资源时。Java中有一个SPI(Service Provider Interface)标准,使用了SPI的库,比如JNDI,JDBC,等等。
这里以JNDI为例,JNDI的类是由bootstarp ClassLoader从rt.jar中间载入的,但是JNDI具体的核心驱动是由正式的实现提供的,并且通常会处于-cp参数之下(注:也就是默认的System ClassLoader管理),这就要求bootstartp ClassLoader去载入只有SystemClassLoader可见的类,正常的逻辑就没办法处理。怎么办呢?
这时JAVA引入了线程上下文类加载的概 念,线程类加载器默认会从父线程继承,如果没有指定的话,默认就是系统类加载器(AppClassLoader),这样的话当加载第三方驱动的时候,就可 以通过线程的上下文类加载器来加载。
Thread两个方法:
public ClassLoader getContextClassLoader() ;
public void setContextClassLoader(ClassLoader cl) ;
我们在加载动态类的时候,可以是在当前上下文环境下的特定的ClassLoader,然后通过当前上下文的ClassLoader去加载动态类,这样就可以摆脱双亲委托模式了。
如果没有通过 setContextClassLoader(ClassLoader cl)
方法进行设置的话,线程将继承其父线程的上下文类加载器。
相关推荐
ClassLoader原理的理解对于深入学习Java和进行系统优化至关重要。这篇博文将带你深入了解ClassLoader的工作机制。 首先,我们来理解ClassLoader的基本概念。ClassLoader是一个抽象的概念,它是Java中的一个接口,...
【图解版】深入分析ClassLoader类加载工作机制,从原理到JVM的装载过程,详情分析了ClassLoader加载类以及自定义类加载器的过程,不可用于商业用途,如有版权问题,请联系删除!
本文将深入探讨ClassLoader的工作原理,源码分析以及实际应用案例。 首先,我们需要理解ClassLoader的基本概念。ClassLoader是Java中的一个核心组件,它遵循"按需加载"的原则,只有当类被引用时才会加载。Java的类...
本示例"ClassLoader小例子"将深入探讨这个概念,并通过一个具体的程序来演示其工作原理。下面我们将详细讨论ClassLoader的基本概念、工作流程以及如何自定义ClassLoader。 1. **ClassLoader的基本概念** - 类加载...
开发者可以通过日志输出、调试工具等方式监控和分析ClassLoader的行为,以解决诸如类找不到、类加载错误等问题。 总之,ClassLoader是Java程序运行的基础,理解其工作原理及特性对于成为一名合格的Java开发者至关...
本文将深入浅出地探讨JVM ClassLoader的工作原理和相关知识点。 首先,ClassLoader可以分为三种基本类型:Bootstrap ClassLoader、Extension ClassLoader和Application ClassLoader。Bootstrap ClassLoader是JVM...
### 完整实例分析 在这个实例中,作者通过实现一个自定义的类加载器`myLoader`,并通过类的全名来加载并实例化对象,这与反射机制类似但又有所不同。 #### 自定义类加载器代码解析 ```java public class ...
1. 源码分析:研究ClassLoader的源码,了解其内部实现,特别是加载类的逻辑。 2. 类加载顺序:观察不同类加载器加载类的顺序,验证双亲委派模型。 3. 动态加载:创建自己的类加载器,尝试动态加载新的类,实现代码的...
本文将深入探讨`ClassLoader`的工作原理、加密解密应用程序以及如何防止类被反编译。 首先,让我们理解`ClassLoader`的基本概念。`ClassLoader`是Java中的一个接口,位于`java.lang`包下,它是Java运行时环境的一...
本文将深入探讨Android一二三代壳加固原理,包括抽取壳、VMP加固以及DEX2C技术,分析其实现源码。 1. **抽取壳原理**: 加固的核心是改变原本的类加载机制,抽取壳的原理就是将原始的.dex文件(包含应用代码)从...
`ClassLoader`的工作原理主要是通过读取`.class`文件并将其转换为`Class`对象。默认情况下,Java使用`Bootstrap ClassLoader`加载`rt.jar`中的核心类,然后`Extension ClassLoader`加载扩展目录下的类,最后`...
在Android系统中,Classloader(类加载器)是至关重要的组件,它负责查找并加载Java类到Dalvik或ART运行时环境。这个测试demo是为了帮助开发者深入理解Android中两种主要的类加载器:DexClassLoader和...
这篇博文主要围绕`Classloader`的`loadClass`方法进行深入探讨,并分析了类加载的细节。我们将通过以下几点来详细解析这个主题: 1. **类加载器的层次结构** Java中的类加载器通常遵循双亲委托模型。当一个类加载...
在Java编程语言中,类加载器(ClassLoader)是一个至关重要的组件,负责将类的字节码从...通过对"my_classloader"项目的探索,我们可以深入学习类加载器的实现原理,这对于理解和扩展自定义的类加载器系统至关重要。
下面,我们将详细探讨Tomcat 5.0.18版本中的ClassLoader工作原理。 首先,ClassLoaders在Java中起着至关重要的作用,它们负责查找和加载类到JVM中。Tomcat的ClassLoader设计独特,因为它需要处理Web应用程序的隔离...
这篇博文深入探讨了ClassLoader的工作原理及其在实际应用中的重要性。以下是ClassLoader的相关知识点: 1. 类加载机制: Java的类加载过程分为加载、验证、准备、解析和初始化五个阶段。ClassLoader主要涉及加载...