zoukankan      html  css  js  c++  java
  • java 详解类加载器的双亲委派及打破双亲委派

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

    https://blog.csdn.net/chang_ge/article/details/80262115

    /www.jb51.net/article/102920.htm

    https://www.cnblogs.com/wxd0108/p/6681618.html

    其实,双亲委派模型并不复杂。自定义类加载器也不难!随便从网上搜一下就能搜出一大把结果,然后copy一下就能用。但是,如果每次想自定义类加载器就必须搜一遍别人的文章,然后复制,这样显然不行。可是自定义类加载器又不经常用,时间久了容易忘记。相信你经常会记不太清loadClassfindClassdefineClass这些函数我到底应该重写哪一个?它们主要是做什么的?本文大致分析了各个函数的流程,目的就是让你看完之后,难以忘记!或者说,延长你对自定义类加载器的记忆时间!随时随地想自定义就自定义!

    1. 双亲委派模型

    关于双亲委派模型,网上的资料有很多。我这里只简单的描述一下,就当是复习。

    1.1 什么是双亲委派模型?

    首先,先要知道什么是类加载器。简单说,类加载器就是根据指定全限定名称将class文件加载到JVM内存,转为Class对象。如果站在JVM的角度来看,只存在两种类加载器:

    • 启动类加载器(Bootstrap ClassLoader):由C++语言实现(针对HotSpot),负责将存放在<JAVA_HOME>lib目录或-Xbootclasspath参数指定的路径中的类库加载到内存中。

    • 其他类加载器:由Java语言实现,继承自抽象类ClassLoader。如:

      • 扩展类加载器(Extension ClassLoader):负责加载<JAVA_HOME>libext目录或java.ext.dirs系统变量指定的路径中的所有类库。
      • 应用程序类加载器(Application ClassLoader)。负责加载用户类路径(classpath)上的指定类库,我们可以直接使用这个类加载器。一般情况,如果我们没有自定义类加载器默认就是用这个加载器。

    双亲委派模型工作过程是:如果一个类加载器收到类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器完成。每个类加载器都是如此,只有当父加载器在自己的搜索范围内找不到指定的类时(即ClassNotFoundException),子加载器才会尝试自己去加载。


    类加载器的双亲委派模型

    1.2 为什么需要双亲委派模型?

    为什么需要双亲委派模型呢?假设没有双亲委派模型,试想一个场景:

    黑客自定义一个java.lang.String类,该String类具有系统的String类一样的功能,只是在某个函数稍作修改。比如equals函数,这个函数经常使用,如果在这这个函数中,黑客加入一些“病毒代码”。并且通过自定义类加载器加入到JVM中。此时,如果没有双亲委派模型,那么JVM就可能误以为黑客自定义的java.lang.String类是系统的String类,导致“病毒代码”被执行。

    而有了双亲委派模型,黑客自定义的java.lang.String类永远都不会被加载进内存。因为首先是最顶端的类加载器加载系统的java.lang.String类,最终自定义的类加载器无法加载java.lang.String类。

    或许你会想,我在自定义的类加载器里面强制加载自定义的java.lang.String类,不去通过调用父加载器不就好了吗?确实,这样是可行。但是,在JVM中,判断一个对象是否是某个类型时,如果该对象的实际类型与待比较的类型的类加载器不同,那么会返回false。

    举个简单例子:

    ClassLoader1ClassLoader2都加载java.lang.String类,对应Class1、Class2对象。那么Class1对象不属于ClassLoad2对象加载的java.lang.String类型。

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

    ①定义Test类。

    1
    2
    3
    4
    5
    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();
        }
      }
     
    }
  • 相关阅读:
    struts2 标签的使用之二 s:iterator
    struts2 标签的使用之一 s:if
    java Tomcat数据库连接池
    JNDI:对java:comp/env的研究
    ***Tomcat7.0安装配置
    ***tomcat配置文件server.xml详解
    宜昌IT软件资源汇总
    tomcat安全设置
    tomcat部署javaweb项目的三种方式
    Tomcat在Linux上的安装与配置
  • 原文地址:https://www.cnblogs.com/handsome1013/p/10759976.html
Copyright © 2011-2022 走看看