双亲委派机制及打破双亲委派示例

双亲委派机制在加载类的时候,会一级一级向上委托,判断是否已经加载,从自定义类加载器-》应用类加载器-》扩展类加载器-》启动类加载器,如果到最后都没有加载这个类,则回去加载自己的类。双亲委托有个弊端:不能向下委派,不能不委派怎么打破双亲委派机制:(也就是能向下委派和不委派)自定义类加载器(不委派)spi机制(向下委派)打破双亲委派打破双亲委派的两种方式:1.通过spi机制,使用ServiceLoader.load去加载2.通过自定义类加载器,继承classloade

大家好,又见面了,我是你们的朋友全栈君。

双亲委派机制

在加载类的时候,会一级一级向上委托,判断是否已经加载,从自定义类加载器-》应用类加载器-》扩展类加载器-》启动类加载器,如果到最后都没有加载这个类,则回去加载自己的类。

双亲委派机制及打破双亲委派示例

双亲委托有个弊端:

不能向下委派,不能不委派

怎么打破双亲委派机制:(也就是能向下委派和不委派)

自定义类加载器(不委派)

spi机制(向下委派)

打破双亲委派

打破双亲委派的两种方式:

1.通过spi机制,使用ServiceLoader.load去加载

2.通过自定义类加载器,继承classloader,重写loadclass方法

SPI机制

spi机制是一种服务发现机制。它通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类。这一机制为很多框架扩展提供了可能,比如在JDBC中就使用到了SPI机制。

为什么通过spi机制就能打破双亲委托?

因为在某些情况下父类加载器需要委托子类加载器去加载class文件。受到加载范围的限制,父类加载器无法加载到需要的文件。

以Driver接口为例,DriverManager通过Bootstrap ClassLoader加载进来的,而com.mysql.jdbc.Driver是通过Application ClassLoader加载进来的。由于双亲委派模型,父加载器是拿不到通过子加载器加载的类的。这个时候就需要启动类加载器来委托子类来加载Driver实现,从而破坏了双亲委派。

SPI机制demo:

public interface HelloService {
    public String getName();
}
public class Hello1 implements HelloService{
    @Override
    public String getName() {
        return "hello1";
    }
}
public class Hello2 implements HelloService{
    @Override
    public String getName() {
        return "hello2";
    }
}

来一个main方法去加载它,将使用ServiceLoader来进行加载

public class SPITest {
    public static void main(String[] args) {
        ServiceLoader<HelloService> serviceLoader = ServiceLoader.load(HelloService.class);
        for (HelloService helloService :serviceLoader){
            System.out.println(helloService.getName());
        }
    }
}

配置文件,文件名为接口的全路径,文件内容为实现类的全路径,如我的为:com.chuan.service.Hello1

双亲委派机制及打破双亲委派示例

输出结果:hello1

只配置了Hello1,所以只发现了这一个实现类。

自定义类加载器

实现逻辑:自定义类继承classLoader,作为自定义类加载器,重写loadClass方法,不让它执行双亲委派逻辑,从而打破双亲委派。

先看一个没有重写的demo

结果:

sun.misc.Launcher$AppClassLoader@58644d46

发现是app类加载器。

然后重写loadClass方法

public class MyClassLoader extends ClassLoader{
    public static void main(String[] args) throws ClassNotFoundException {
        // ServiceLoader.load()
        MyClassLoader myClassLoader = new MyClassLoader();
        Class<?> aClass = myClassLoader.loadClass(Test1.class.getName());
        System.out.println(aClass.getClassLoader());
    }
    protected Class<?> findClass(String className) throws ClassNotFoundException {
        System.out.println("My findClass!");
        return null;
    }

    protected Class<?> loadClass(String name, boolean resolve)
            throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    //修改classloader的原双亲委派逻辑,从而打破双亲委派
                    if (name.startsWith("com.chuan")){
                        c=findClass(name);
                    }
                    else {
                        c=this.getParent().loadClass(name);
                    }
                } catch (ClassNotFoundException e) {
                }

                if (c == null) {
                    long t1 = System.nanoTime();
                    c = findClass(name);
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }
}
class Test1{

        }

运行报错,因为没有委派给app类加载器,所以找不到加载不了这个类。

那么新的问题又来了,如果我自定义类记载器和核心类重名怎么办,该怎么加载,又或者我想篡改核心类内容,jvm又是怎么解决的?

jvm肯定解决了这个问题,openjdk源码在AccessController.doPrivileged

学名叫做沙箱安全机制,主要作用是:保护核心类,防止打破双亲委派机制,防篡改,如果重名的话就报异常,这里的重名指包名加类名都重复。

demo:

package java.lang;

public class Integer {
    public static void main(String[] args) {
        System.out.println("1");
    }
}

运行报错:

错误: 在类 java.lang.Integer 中找不到 main 方法, 请将 main 方法定义为:

public static void main(String[] args)

否则 JavaFX 应用程序类必须扩展javafx.application.Application

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/126938.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 计算机桌面锁写快捷,锁定计算机快捷键_锁定计算机的快捷键

    计算机桌面锁写快捷,锁定计算机快捷键_锁定计算机的快捷键2017-01-0515:11:14当有事需暂时离开,又不想他人动用电脑时,可采取锁定桌面的办法。1、首先要在电脑中设置好一个用户帐户密码(开机密码)。在锁定桌面时,只需按Win十L键(即按住那个有微软视窗图标的键…2016-12-1814:43:081在电脑上键盘上找到Windows键和L键,这两个键就是快速锁定计算机的关键所在步骤阅读2两只手一只手按Windows键,另一只手按L键,…

    2022年7月21日
    12
  • slf4j如何使用_正确刷牙方法图解

    slf4j如何使用_正确刷牙方法图解环境说明Windows101803IDEA2018.2.EAPMaven3.5.2这是正文POM文件&lt;!–https://mvnrepository.com/artifact/org.projectlombok/lombok–&gt;&lt;dependency&gt;&lt;groupId&g…

    2022年9月5日
    3
  • 这是一篇日记而已

    这是一篇日记而已2018年6月27日                                          星期三                                       晴(巨热)刚刚装订好C++的课设报告,瞬间感觉轻松了不少,因为今天四点顺利的考完了并行计算,加上周二交上的十五六页Oracle结课报告,本学期的三门选修课都已经顺利结课。从明天起,基本上已经进入了没课…

    2022年5月7日
    37
  • 在ThinkPHP中,if标签和比较标签对于变量的比较。

    在ThinkPHP中,if标签和比较标签对于变量的比较。

    2021年9月18日
    39
  • css中100vh 加减运算[通俗易懂]

    css中100vh 加减运算[通俗易懂]vh/vwvh:相对于视窗的高度,视窗被均分为100单位的vh;vw:相对于视窗的宽度,视窗被均分为100单位的vw;vmax:相对于视窗的宽度或高度中较大的那个。其中最大的那个被均分为100单位的vmax;vmin:相对于视窗的宽度或高度中较小的那个。其中最小的那个被均分为100单位的vmin;视区所指为浏览器内部的可视区域大小,即window.innerWidth/window.innerHeight大小,不包含任务栏标题栏以及底部工具栏的浏览器区域大小。calcca…

    2022年5月5日
    53
  • Linux dstat 监控工具[通俗易懂]

    Linux dstat 监控工具[通俗易懂]简述dstat是一个可以取代vmstat,iostat,netstat和ifstat这些命令的多功能产品。dstat克服了这些命令的局限并增加了一些另外的功能,增加了监控项,也变得更灵活了。dstat可以很方便监控系统运行状况并用于基准测试和排除故障。dstat可以让你实时地看到所有系统资源,例如,你能够通过统计IDE控制器当前状态来比较磁盘利用率,或者直接通过网络带宽数值来比较磁盘…

    2022年6月18日
    28

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号