打破双亲委派机制有什么用_双亲委派机制及打破双亲委派示例

双亲委派机制

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

双亲委托有个弊端:

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

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

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

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 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

标签:委派,String,示例,public,双亲,class,加载

来源: https://blog.csdn.net/qq_39404258/article/details/112065471

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值