`

5、java类加载器

    博客分类:
  • java
 
阅读更多
讲解之前先引入几个问题:
1.jvm如何识别.class文件?
2.jvm如何加载.class文件里面的字节码?
3.jvm如何创建类、对象、方法、属性?

jvm是通过.class文件的二进制流转换成16进制,得到前8位字符串cafebabe认为这是一个.class文件;


由上图可知类加载器是JVM的一部分,主要作用是将字节码加载进入执行引擎,以供执行。当调用java.exe执行一个.class文件时,从而根据%JAVA_HOME%\jre\lib\i386\jvm.cfg配置来选择激活jvm,初始化工作完成之后便启动Bootstrap Loader(引导类)加载器,它由C++编写。JVM中另外两个内置类加载器是ExtClassLoader和AppClassLoader,它们定义在sun.misc.Launcher.class中,为内部类,且由Bootstrp Loader加载进入虚拟机。
启动(Bootstrap)类加载器:引导类装入器是用本地代码实现的类装入器,它负责将 <Java_Runtime_Home>/lib 下面的类库加载到内存中。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作。
标准扩展(Extension)类加载器:扩展类加载器是由 ExtClassLoader(sun.misc.Launcher$ExtClassLoader) 实现的。它负责将 < Java_Runtime_Home >/lib/ext 或者由系统变量 java.ext.dir 指定位置中的类库加载到内存中。开发者可以直接使用标准扩展类加载器。
系统(System)类加载器:系统类加载器是由 AppClassLoader(sun.misc.Launcher$AppClassLoader)实现的。它负责将系统类路径(CLASSPATH)中指定的类库加载到内存中。开发者可以直接使用系统类加载器。
除了以上列举的三种类加载器,还有一种比较特殊的类型就是线程上下文类加载;

类加载器也是Java类,本身也要被类加载器加载,第一个类加载器不是java类,而是BootStrap。Java虚拟机中的所有类装载器采用具有父子关系的树形结构进行组织,在实例化每个类装载器对象时,需要为其指定一个父级类装载器对象或者默认采用系统类装载器为其父级类加载。

1、类加载器的委派模型:假设AppClassLoader需要加载一个类,它会首先委托其父加载器ExtClassLoader来加载此类,ExtClassLoader也会递归性的委托其父加载器Bootstrap Loader来加载此类,如果Bootstrap Loader在sun.boot.class.path下找到被加载类时即加载,如果无法找到时再依次由子类加载器去加载。委派模型是针对Java安全而设计的,这也印证了Java语言的设计初衷:面向网络的编程语言。
2、由同一个类加载器所加载的类只能引用该加载器和其父加载器所加载的其他类。 

两种方法可以在运行时动态加载.class文件:1) Class clazz = Class.forName("类名称"); 2) 自定义类加载器,然后调用loadClass(“类名称”)方法;
一般来说,Java 虚拟机使用 Java 类的方式如下:Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件)。类加载器负责读取 .class 文件,并转换成 java.lang.Class类的一个实例。每个这样的实例用来表示一个 Java 类。通过此实例的 new Instance()方法就可以创建出该类的一个对象。

基本上所有的类加载器都是 java.lang.ClassLoader类的一个实例。

&:类是Class的实例,类加载器是抽象类ClassLoader的实例(除了启动类加载器BootStrap);

java.lang.ClassLoader类的基本功能就是根据一个指定的类名称,找到或者生成其对应的字节码(.class文件),然后从这些字节代码中创建一个 java.lang.Class类的一个实例。除此之外,ClassLoader还负责加载 Java 应用所需的资源,如图像文件和配置文件等。ClassLoader 中与加载类相关的方法:

//加载指定名称(包括包名)的二进制类型,供用户调用的接口    
public Class<?> loadClass(String name) throws ClassNotFoundException{ … }   
   
//加载指定名称(包括包名)的二进制类型,同时指定是否解析(但是这里的resolve参数不一定真正能达到解析的效果),供继承用    
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{ … }   
  
//findClass方法一般被loadClass方法调用去加载指定名称类,供继承用    
protected Class<?> findClass(String name) throws ClassNotFoundException { … }   
 
//定义类型,一般在findClass方法中读取到对应字节码后调用,可以看出不可继承    
//(说明:JVM已经实现了对应的具体功能,解析对应的字节码,产生对应的内部数据结构放置到方法区,所以无需覆写,直接调用就可以了)    
protected final Class<?> defineClass(String name, byte[] b, int off, int len) throws ClassFormatError{ … }  
不同的类加载器为相同名称的类创建了不同的名称空间。相同名称的类可以并存在 Java 虚拟机中,只需要用不同的类加载器来加载它们即可。不同类加载器加载的类之间是不兼容的,这就相当于在 Java 虚拟机内部创建了一个个相互隔离的 Java 类空间

类加载器   加载类的时候,首先会让父类加载器来加载这个类。这就意味着:完成类的加载工作的类加载器和启动这个加载过程的类加载器(&:加载一个类是分为两个步骤完成的),可能不是同一个。真正完成类的加载工作是通过调用 defineClass来实现的;而启动类的加载过程是通过调用 loadClass来实现的。前者称为一个类的定义加载器(defining loader),后者称为初始加载器(initiating loader)。在 Java 虚拟机判断两个类是否相同的时候,使用的是类的定义加载器。也就是说,是哪个类加载器启动了的加载过程并不重要,重要的是最终是那个类加载器定义了这个类。
方法 loadClass()抛出的是 java.lang.ClassNotFoundException异常;方法 defineClass()抛出的是 java.lang.NoClassDefFoundError异常。
Java代码  收藏代码
public static void main(String[] args) {   
        try {   
            System.out.println(ClassLoader.getSystemClassLoader());   
            System.out.println(ClassLoader.getSystemClassLoader().getParent());   
           System.out.println(ClassLoader.getSystemClassLoader().getParent().getParent());   
        } catch (Exception e) {   
            e.printStackTrace();   
        }   
}   
sun.misc.Launcher$AppClassLoader@6d06d69c   
sun.misc.Launcher$ExtClassLoader@70dea4e   
null   
通过以上的代码输出,我们可以判定系统类加载器的父加载器是标准扩展类加载器,但是我们试图获取标准扩展类加载器的父类加载器时确得到了null,由于jvm对于父类加载器是引导类加载器的情况,getParent()方法返回 null。
我们首先看一下java.lang.ClassLoader抽象类中默认实现的两个构造函数(大楷意思,与源码不太一致):标准扩展类加载器和系统类加载器及其父类(java.net.URLClassLoader和java.security.SecureClassLoader)都没有覆写java.lang.ClassLoader中默认的加载委派规则---loadClass(…)方法。
Java代码  收藏代码
public Class<?> loadClass(String name) throws ClassNotFoundException {   
    return loadClass(name, false);   
}    
protected synchronized Class<?> loadClass(String name, boolean resolve)   
        throws ClassNotFoundException {   
    // 首先判断该类型是否已经被加载   
    Class c = findLoadedClass(name);   
    if (c == null) {   
        //如果没有被加载,就委托给父类加载或者委派给启动类加载器加载   
        try {   
            if (parent != null) {   
                //如果存在父类加载器,就委派给父类加载器加载   
                c = parent.loadClass(name, false);   
            } else {   
                //如果不存在父类加载器,就检查是否是由启动类加载器加载的类,   
                //通过调用本地方法native findBootstrapClass0(String name)   
                c = findBootstrapClass0(name);   
            }   
        } catch (ClassNotFoundException e) {   
            // 如果父类加载器和启动类加载器都不能完成加载任务,才调用自身的加载功能   
            c = findClass(name);   
        }   
    }   
    if (resolve) {   
        resolveClass(c);   
    }   
    return c;   
}  
类加载器在成功加载某个类之后,会把得到的 java.lang.Class类的实例缓存起来。下次再请求加载该类的时候,类加载器会直接使用缓存的类的实例,而不会尝试再次加载。也就是说,对于一个类加载器实例来说,相同全名的类只加载一次,即 loadClass方法不会被重复调用。

动态加载类:
1.调用class.forName(String);
public static Class<?> forName(String className) 
               throws ClassNotFoundException { 
       return forName0(className, true, ClassLoader.getCallerClassLoader());     //ClassLoader 
   } 
设置为true表示强制加载同时完成初始化。例如典型的就是利用DriverManager进行JDBC驱动程序类注册的问题。因为每一个JDBC驱动程序类的静态初始化方法都用DriverManager注册驱动程序,这样才能被应用程序使用。这就要求驱动程序类必须被初始化,而不单单被加载。Class.forName的一个很常见的用法就是在加载数据库驱动的时候。如 Class.forName("org.apache.derby.jdbc.EmbeddedDriver").newInstance()用来加载 Apache Derby 数据库的驱动。


2.自定义类加载器
通过前面的分析,除了启动类加载器之外,标准扩展类加载器和系统类加载器都可以当做自定义类加载器,唯一区别是是否被虚拟机默认使用。自定义的类加载器必须符合以下要求:
     继承ClassLoader类;
     覆写findClass方法

Java代码  收藏代码
package org.Smart; 
 
import java.io.ByteArrayOutputStream; 
import java.io.File; 
import java.io.FileInputStream; 
import java.io.IOException; 
import java.io.InputStream; 
 
public class FileSystemClassLoader extends ClassLoader {  
    private String rootDir;  
    public FileSystemClassLoader(String rootDir) {  
        this.rootDir = rootDir;  
    }  
    protected Class<?> findClass(String name) throws ClassNotFoundException {  
        byte[] classData = getClassData(name);  
        if (classData == null) {  
            throw new ClassNotFoundException();  
        }  
        else {  
            return defineClass(name, classData, 0, classData.length);   //ClassLoader里的方法:创建对象 
        }  
    }  
    private byte[] getClassData(String className) {  
        String path = classNameToPath(className);  
        try {  
            InputStream ins = new FileInputStream(path);  
            ByteArrayOutputStream baos = new ByteArrayOutputStream();  
            int bufferSize = 4096;  
            byte[] buffer = new byte[bufferSize];  
            int bytesNumRead = 0;  
            while ((bytesNumRead = ins.read(buffer)) != -1) {  
                baos.write(buffer, 0, bytesNumRead);  
            }  
            return baos.toByteArray();  
        } catch (IOException e) {  
            e.printStackTrace();  
        }  
        return null;  
    }  
    String classNameToPath(String className) { 
        StringBuffer s=new  StringBuffer(rootDir+"/"+className.replace('.', '/')+".class"); 
        return s.toString(); 
    }  

测试: 
public static void main(String...s) throws ClassNotFoundException { 
        @SuppressWarnings("rawtypes") 
        Class app=new FileSystemClassLoader("E:/side1/Smart/target/classes").findClass("org.Smart.App"); 
        System.out.println(app.getClassLoader());          //org.Smart.FileSystemClassLoader@3020ad 
        System.out.println(App.class.getClassLoader());    //sun.misc.Launcher$AppClassLoader@15253d5 
    } 
自定义的类加载器测试成功!!

   

线程上下文类加载器(context class loader)
是从 JDK 1.2 开始引入的。类 java.lang.Thread中的方法 getContextClassLoader()和 setContextClassLoader(ClassLoader cl)用来获取和设置线程的上下文类加载器。如果没有通过 setContextClassLoader(ClassLoader cl)方法进行设置的话,线程将继承其父线程的上下文类加载器。Java 应用运行的初始线程的上下文类加载器是系统类加载器。在线程中运行的代码可以通过此类加载器来加载类和资源。

Java 提供了很多服务提供者接口(Service Provider Interface,SPI),允许第三方为这些接口提供实现。常见的 SPI 有 JDBC、JCE、JNDI、JAXP 和 JBI 等。这些 SPI 的接口由 Java 核心库来提供,如 JAXP 的 SPI 接口定义包含在 javax.xml.parsers包中。这些 SPI 的实现代码很可能是作为 Java 应用所依赖的 jar 包被包含进来,可以通过类路径(CLASSPATH)来找到,SPI 的接口是 Java 核心库的一部分,是由引导类加载器来加载的;SPI 实现的 Java 类一般是由系统类加载器来加载的。引导类加载器是无法找到 SPI 的实现类的,因为它只加载 Java 的核心库。它也不能代理给系统类加载器,因为它是系统类加载器的祖先类加载器。
     Java默认的线程上下文类加载器是系统类加载器(AppClassLoader)。在 SPI 接口的代码中使用线程上下文类加载器,就可以成功的加载到 SPI 实现的类。线程上下文类加载器在很多 SPI 的实现中都会用到。
后续:

http://blog.csdn.net/zhoudaxia/article/details/35897057
  • 大小: 15.9 KB
分享到:
评论

相关推荐

    java类加载器

    ### Java 类加载器详解 #### 一、类加载器概述 在Java中,类加载器(Class Loader)是一项核心机制,用于将字节码(.class文件)加载到JVM中,使其成为运行时的对象。类加载器不仅实现了类的加载功能,还确保了...

    Java类加载器原理

    Java 类加载器原理 Java 类加载器是Java虚拟机(JVM)的核心组成部分,它负责将类的字节码加载到内存中并转换为可执行的Java类。类加载器的作用不仅仅是加载类,还包括确保类的唯一性,避免重复加载,并且遵循特定...

    java类加载器实例

    类加载器是 Java 语言的一个创新,也是 Java 语言流行的重要原因之一。它使得 Java 类可以被动态加载到 Java 虚拟机中并执行。类加载器从 JDK 1.0 就出现了,最初是为了满足 Java Applet 的需要而开发出来的。Java ...

    java 类加载器 加密

    Java 类加载器是Java运行时环境的一个重要组成部分,它的主要职责是将编译后的字节码(.class文件)加载到JVM中,使得程序能够运行。类加载器的机制保证了类的唯一性,同时也提供了灵活性,允许我们自定义加载逻辑。...

    Java类加载器.pdf

    ### Java类加载器详解 Java类加载器是Java运行时环境的一个关键组成部分,负责将类文件(.class)从各种来源加载到JVM中。它不仅管理类的生命周期,还确保了类的正确加载和初始化,是Java动态特性的基石。 #### 类...

    自定义Java类加载器

    Java类加载器是Java虚拟机(JVM)的关键组成部分,它负责查找并加载类到内存中,使得程序能够运行。自定义Java类加载器允许我们根据特定需求扩展默认的加载机制,例如,从非标准位置加载类或者实现动态加载。在Java...

    java类加载器-tomcat中的类加载器

    Java 类加载器是Java虚拟机(JVM)的核心组成部分,它负责将编译后的字节码文件(.class文件)加载到JVM中并转换为运行时的数据结构。Tomcat,作为广泛使用的Java Servlet容器,它自定义了一套类加载机制,以满足Web...

    Java的类加载器

    Java的类加载器是Java虚拟机(JVM)的核心组件之一,它负责将类的字节码文件从文件系统或网络中加载到JVM中,然后进行校验、解析和初始化,使得Java程序能够运行。类加载器的概念是Java动态加载和运行时类重定义的...

    类加载器文件

    **类加载器(ClassLoader)**是Java虚拟机(JVM)中的一个重要组成部分,它负责将编译好的`.class`文件加载到JVM中,使得这些类可以在Java环境中运行。类加载器不仅能够加载类,还能够根据不同的需求定制加载方式,如从...

    java类加载器学习三、类加载器的委托模式

    java类加载器学习三、类加载器的委托模式

    类加载器(java)

    Java中的类加载器是JVM(Java虚拟机)的核心组件之一,它们负责将.java源代码编译成的.class字节码文件加载到JVM中,从而使得程序能够运行。类加载器不仅涉及到程序的正常执行,还与Java的动态加载、模块化系统以及...

    深入探讨 Java 类加载器

    【深入探讨 Java 类加载器】 Java 类加载器是Java虚拟机(JVM)的核心组成部分,它的主要任务是将Java类的字节码加载到JVM中以便执行。类加载器的概念始于JDK 1.0,最初是为了解决Java Applet在浏览器中的运行需求...

    java应用程序类加载器,ClassLoader for java Application

    Java应用程序类加载器是Java平台的核心组成部分之一,它负责加载应用程序中的类到JVM(Java虚拟机)中,使得程序能够执行。理解类加载器的工作原理对于深入学习Java编程至关重要。在Java中,类加载器按照层次结构...

    深入解析Java类加载器及其工作机制

    Java 类加载器是 Java 运行时环境中不可或缺的组成部分,负责将 .class 文件加载到 JVM 中。理解类加载器的工作原理对于深入掌握 Java 语言及其运行机制至关重要。本文将详细探讨 Java 类加载器的概念、类型以及工作...

    关于Java类加载器的探讨.pdf

    扩展类加载器是Java实现的,是一个真正的Java类加载器,负责加载ire/lib/ext目录下的类,并且可以通过Java程序获得这个类加载器。 3. 系统类加载器:负责加载classpath中的类,也就是执行Java MainClass时加载Main...

    深入探讨 Java 类加载器.wps

    深入探讨 Java 类加载器.wps深入探讨 Java 类加载器.wps深入探讨 Java 类加载器.wps

    java类加载器1

    Java 类加载器是Java语言的核心特性之一,它允许程序在运行时动态地加载类到Java虚拟机(JVM)中。这一特性对于实现模块化、插件化和动态部署等功能至关重要。类加载器的设计遵循“分层加载”原则,形成了一个树状的...

    Java类加载内幕详细讲解

    通过掌握类加载的过程、类加载器的工作原理以及类加载的线程安全性等方面的知识,开发者能够更好地利用Java的动态特性,优化程序性能,并避免常见的异常问题,如`ClassNotFoundException`等。此外,对于自定义类加载...

Global site tag (gtag.js) - Google Analytics