类加载器的双亲委派及打破双亲委派

一般的场景中使用Java默认的类加载器即可,但有时为了达到某种目的又不得不实现自己的类加载器,例如为了达到类库的互相隔离,例如为了达到热部署重加载功能。这时就需要自己定义类加载器,每个类加载器加载各自的类库资源,以此达到资源隔离效果。在对资源的加载上可以沿用双亲委派机制,也可以打破双亲委派机制。

一、沿用双亲委派机制自定义类加载器很简单,只需继承ClassLoader类并重写findClass方法即可。如下例子:

①先定义一个待加载的类Test,它很简单,只是在构建函数中输出由哪个类加载器加载。

public class Test {

    public Test(){
        System.out.println(this.getClass().getClassLoader().toString());
    }

}

②定义一个TestClassLoader类继承ClassLoader,重写findClass方法,此方法要做的事情是读取Test.class字节流并传入父类的defineClass方法即可。然后就可以通过自定义累加载器TestClassLoader对Test.class进行加载,完成加载后会输出“TestLoader”。

public class TestClassLoader extends ClassLoader {

    private String name;

    public TestClassLoader(ClassLoader parent, String name) {
        super(parent);
        this.name = name;
    }

    @Override
    public String toString() {
        return this.name;
    }

    @Override
    public Class<?> findClass(String name) {

        InputStream is = null;
        byte[] data = null;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            is = new FileInputStream(new File("d:/Test.class"));
            int c = 0;
            while (-1 != (c = is.read())) {
                baos.write(c);
            }
            data = baos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
                baos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return this.defineClass(name, data, 0, data.length);
    }

    public static void main(String[] args) {
        TestClassLoader loader = new TestClassLoader(
                TestClassLoader.class.getClassLoader(), "TestLoader");
        Class clazz;
        try {
            clazz = loader.loadClass("test.classloader.Test");
            Object object = clazz.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

二、打破双亲委派机制则不仅要继承ClassLoader类,还要重写loadClass和findClass方法,如下例子:

①定义Test类。

public class Test {
    public Test(){
        System.out.println(this.getClass().getClassLoader().toString());
    }
}

②重新定义一个继承ClassLoader的TestClassLoaderN类,这个类与前面的TestClassLoader类很相似,但它除了重写findClass方法外还重写了loadClass方法,默认的loadClass方法是实现了双亲委派机制的逻辑,即会先让父类加载器加载,当无法加载时才由自己加载。这里为了破坏双亲委派机制必须重写loadClass方法,即这里先尝试交由System类加载器加载,加载失败才会由自己加载。它并没有优先交给父类加载器,这就打破了双亲委派机制。

public class TestClassLoaderN extends ClassLoader {

    private String name;

    public TestClassLoaderN(ClassLoader parent, String name) {
        super(parent);
        this.name = name;
    }

    @Override
    public String toString() {
        return this.name;
    }

    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        Class<?> clazz = null;
        ClassLoader system = getSystemClassLoader();
        try {
            clazz = system.loadClass(name);
        } catch (Exception e) {
            // ignore
        }
        if (clazz != null)
            return clazz;
        clazz = findClass(name);
        return clazz;
    }

    @Override
    public Class<?> findClass(String name) {

        InputStream is = null;
        byte[] data = null;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            is = new FileInputStream(new File("d:/Test.class"));
            int c = 0;
            while (-1 != (c = is.read())) {
                baos.write(c);
            }
            data = baos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
                baos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return this.defineClass(name, data, 0, data.length);
    }

    public static void main(String[] args) {
        TestClassLoaderN loader = new TestClassLoaderN(
                TestClassLoaderN.class.getClassLoader(), "TestLoaderN");
        Class clazz;
        try {
            clazz = loader.loadClass("test.classloader.Test");
            Object object = clazz.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
时间: 2024-08-05 11:26:05

类加载器的双亲委派及打破双亲委派的相关文章

深入理解JVM虚拟机7:JNDI,OSGI,Tomcat类加载器实现

打破双亲委派模型 JNDI JNDI 的理解 JNDI是 Java 命名与文件夹接口(Java Naming and Directory Interface),在J2EE规范中是重要的规范之中的一个,不少专家觉得,没有透彻理解JNDI的意义和作用,就没有真正掌握J2EE特别是EJB的知识. 那么,JNDI究竟起什么作用?//带着问题看文章是最有效的 要了解JNDI的作用,我们能够从“假设不用JNDI我们如何做?用了JNDI后我们又将如何做?”这个问题来探讨. 没有JNDI的做法: 程序猿开发时,

java类加载器——ClassLoader

Java的设计初衷是主要面向嵌入式领域,对于自定义的一些类,考虑使用依需求加载原则,即在程序使用到时才加载类,节省内存消耗,这时即可通过类加载器来动态加载. 如果你平时只是做web开发,那应该很少会跟类加载器打交道,但如果你想深入学习tomcat服务器的架构,它是必不可少的.所谓类加载器,就是用于加载Java类到Java虚拟机中,它负责读取Java字节码,并转换成java.lang.Class类的一个实例,使字节代码.class文件得以运行.一般类加载器负责根据一个指定的类找到对应的字节代码,然

JAVA-类加载机制(3)-类加载器

类加载器 作用:通过类全限定名来获取二进制字节流 用例:类层次划分,OSGi,热部署,代码加密 1,类和类加载器 任意一个类和加载该类的加载器一同确立在虚拟机中的唯一性: 每个类拥有独立的类名称空间: 判断两个类是否相等,必须建立在同一个类加载器加载的前提下:否则会影响:equals, isAssignableFrom, isInstance, instanceof结果 package com.classload.temp; import java.io.IOException; import

Tomcat-正统的类加载器架构

主流的Java Web服务器主要有tomcat,Jetty,WebLogic,WebSphere等,这些服务器都实现了自己定义的加载器(一般都有一个或者多个),因为一个功能齐全的服务器,都需要解决如下问题: 部署在同一个服务器上的两个Web应用程序使用的Java 类库可以实现相互隔离,这是最基本的要求.两个不同应用程序可能会依赖同一个第三方类库的不同版本的,不能要求一个类库在一个服务器中只有一份,服务器应当保证两个应用程序的类库可以互相独立使用 部署在同一个服务器上的两个Web应用程序所使用的J

Java类加载器及Android类加载器基础

引子 Android插件化与热更新技术日渐成熟,当你研究这些技术时会发现类加载器在其中占据重要地位.Java语言天生就有灵活性.动态性,支持运行期间动态组装程序,而这一切的基础就是类加载器. Java中的类加载器 Java灵活性和动态性的原因 Java源代码被编译器编译成字节码,即从.java文件编译为.class文件,而.class文件就是通过类加载器加载到虚拟机内存中的. 虚拟机的类加载(Class Loading)过程分为加载.链接(验证.准备.解析).初始化.使用.卸载等过程.这里仅考虑

Tomcat类加载器

1JVM类加载机制 JVM的ClassLoader通过Parent属性定义父子关系,可以形成树状结构.其中引导类.扩展类.系统类三个加载器是JVM内置的. 它们的作用分别是: 1)引导类加载器:使用native代码实现,在rt.jar等包中搜索运行JVM所需的类,例如java.lang等包下的类. 2)扩展类加载器:负责载入标准扩展目录中的类,例如Sun的JVM的扩展目录是/jdk/jre/lib/ext. 3)系统类加载器:默认的类加载器,搜索环境变量CLASSPATH中指明的路径. 2双亲委

Java内存管理-掌握自定义类加载器的实现(七)

勿在流沙筑高台,出来混迟早要还的. 做一个积极的人 编码.改bug.提升自己 我有一个乐园,面向编程,春暖花开! 上一篇分析了ClassLoader的类加载相关的核心源码,也简单介绍了ClassLoader的设计思想,读源码相对来说是比较枯燥的,还是这个是必须要走的过程,学习源码中的一些思想,一些精髓,看一下大神级人物是怎么写出那么牛逼的代码.我们能够从中学到一点点东西,那也是一种进步和成长了.本文基于上一篇文章内容,手把手写一个自定义类加载器,并且通过一些简单的案例(场景)让我们更加细致和静距

java jvm虚拟机类加载器

在Java中任意一个类都是由这个类本身和加载这个类的类加载器来确定这个类在JVM中的唯一性. 类加载器 虚拟机设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到Java虚拟机外部去实现, 以便让应用程序自己决定如何去获取所需要的类. 实现这个动作的代码模块称为“类加载器”. 类与类加载器 类加载器虽然只用于实现类的加载动作, 但它在Java程序中起到的作用却远远不限于类加载阶段. 对于任意一个类, 都需要由加载它的类加载器和这个类本身一同确立其在Java虚拟

jvm类加载器和双亲委派模型

类加载器按照层次,从顶层到底层,分为以下三种: (1)启动类加载器(Bootstrap ClassLoader) 这个类加载器负责将存放在JAVA_HOME/lib下的,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别的类库加载到虚拟机内存中.启动类加载器无法被Java程序直接引用. (2)扩展类加载器(Extension ClassLoader) 这个加载器负责加载JAVA_HOME/lib/ext目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有