跳至主要內容

JVM (四) 打破双亲委派和 SPI 机制

安图新大约 2 分钟

JVM (四) 打破双亲委派和 SPI 机制

前言:

我们都知道判断两个类是不是同一个,要根据类加载器和全限定名。这是为什么呢?为什么不同的类加载器加载同一个类是不同的呢?

答案就是,不同的类加载器所加载的类在方法区的存储空间是不同的即 InstanceKlass 的不同。不同类加载器之间的空间是分隔开的。同一个类在一个类加载器中只会加载一次。

双亲委派的弊端,无法做到不委派,也无法向下委派。

沙箱安全:

虽然 JVM 让我们用一些方式打破双亲委派,但是对于系统的核心类库 JVM 是会进行保护不让篡改的。如果自己写了和核心类库相同的类,在运行的时候会出错。

一:自定义类加载器打破双亲委派,不委派双亲

我们从上篇中有介绍 classLoader.loadClass 的时候会进行双亲委派进行加载,如果双亲都找不到指定类会调用 findClass 方法。

classLoader 类中的 loadClass 有默认的实现就是双亲委派逻辑,findClass 没有默认的实现需要自定义类加载器来实现。

所以如果只是使用一个自定义类加载器而不打破双亲委派,只要继承 ClassLoader 来重写 findClass。如果想打破双亲委派,也要重写 loadClass 方法了,做到不委派。

二:SPI 机制向下委派

SPI,全称为 Service Provider Interface,是一种服务发现机制。它通过在 ClassPath 路径下的 META-INF/services 文件夹查找文件,自动加载文件里所定义的类。

这一机制为很多框架扩展提供了可能,比如在 Dubbo、JDBC 中都使用到了 SPI 机制。我们先通过一个很简单的例子来看下它是怎么用的。SPI 是实现向下委派的。

1:接口类模块

就一个接口,让其它模块通过 Maven 引用。

 
 

2:第一个接口实现类模块

只有一个实现类,在 resources 下面增加 META-INF/services 文件夹,文件夹下面增加一个文本文件,文件名是实现的接口全路径,文件里面的内容是实现的类的全路径。

 
 
 
 

3:第二个接口实现类模块

 
 
 
 

接下来我们开始使用 SPI 机制

 
 

我们在 pom 中引入第一个实现类模块:

 
 

上述 main 方法执行的结果就是对应实现类的执行结果。

 
 

当引入多个实现类的时候,都可以执行。

 
 
上次编辑于:
贡献者: Andy