为什么说java spi破坏双亲委派模型?

虽然有SPI破坏双亲委派模型的说法,但我不太认同。简单说下。

双亲委派模型(再次吐槽下这个翻译),是一种加载类的约定。这个约定的一个用处是保证安全。比如说你写Java用了String类,你怎么保证你用的那个String类就是JDK里提供的那个String类呢?答案是对于JDK基础类,JDK要用特殊的ClassLoader来保证在正确的位置加载。JDK主要有3个自带ClassLoader:

•最基础:Bootstrap ClassLoader(加载JDK的/lib目录下的类)次基础:Extension

•ClassLoader(加载JDK的/lib/ext目录下的类)

•普通:Application ClassLoader(程序自己classpath下的类)

双亲委派模型要求如果一个类可以被委派最基础的ClassLoader加载,就不能让高层的ClassLoader加载。这样你就知道你用的String类一定是被BootstrapClasserLoader加载的/lib下的那个rt.jar的那个java/lang/String.class.

但这个模型不是强制的。如果你自己写个自己的ClassLoader,你可以不理会它。比如你可以写个自己的ClassLoader去自己规定的一个神怪的目录里加载自己写的String.class。当然Java Runtime能够识别出这俩String不是一个类,哪怕他们的Full Qualified Class Name一模一样。所以如果你这做了,大概率是自作自受。当如果你真的知道自己在干啥,是能够玩出一些花的。

顺便说一句,这个机制的安全性是有限的。假如有人能登入服务器,能够直接替换JDK目录的文件。上述机制也就失效了。为了保证严格的安全,还应该保证系统文件要做数字签名。

另外一点是,这个模式虽然“安全“,但是损失了一丢丢灵活性。就比如java.sql.Driver这个东西。JDK只能提供一个规范接口,而不能提供实现。提供实现的是实际的数据库提供商。提供商的库总不能放JDK目录里吧。

ava从1.6搞出了SPI就是为了优雅的解决这类问题——JDK提供接口,供应商提供服务。编程人员编码时面向接口编程,然后JDK能够自动找到合适的实现,岂不是很爽?

但是便利的同时也带来了困扰。提供商提供的类不能放JDK里的lib目录下,所以也就没法用BootstrapClassLoader加载了。所以当你代码写了

扫描二维码关注公众号,回复: 6589855 查看本文章

Class clz = Class.forName("java.sql.Driver"); Driver d = (Driver)clz.newInstance();

时,这个代码会用Bootstrap ClassLoader尝试去加载.问题是java.sql.Driver是个接口,无法真的实例化,就报错了。

没有SPI时,你可以现在classpath里加一个mysql-connector-java.jar,然后这样写

Class clz = Class.forName("com.mysql.jdbc.Driver"); Driver d = (Driver) clz.newInstance();

这就没问题了,这里用了Application Classloader加载了mysql-connector-java.jar的com.mysql.jdbc.Driver。问题是你hard code了一定要加载"com.mysql.jdbc.Driver",不是很优雅,不能实现“用接口编程,自动实例化真的实现“的这种编码形式。

使用SPI后,代码大致会这样

Connection connection = DriverManager.getConnection("jdbc:mysql://xxxxxx/xxx", "xxxx", "xxxxx");

DriverManager就根据"jdbc:mysql"这个提示去找具体实现去了。

然后

System.out.println(connection.getClass().getClassLoader());

就会看到这里的结果是Application ClassLoader。这就好像Application ClassLoader加载了本来应该由BootstrapClassLoader加载的java.sql.Connection一样。看起来像是违反了双亲委派模型。但实际上,这里的Connection的类型实际上是“com.mysql.jdbc.JDBC4Connection“,也是个第三方类。AppClassLoader加载一个第三方类看起来并没有违反模型。

再进一步调查下Connection接口自己的加载情况:

System.out.println(java.sql.Connection.class.getClassLoader());

会发现返回的null。说明Connection自己是被Bootstrap ClassLoader加载的。

综上,并没有说Bootstrap ClassLoader加载了个第三方库或者Application ClassLoader加载了JDK的库的情况发生。

所以能否请题主给出具体哪里写了“SPI破坏双亲委派模型“?我再仔细看看是不是前后哪里谁理解错了。可能是我错了,也可能是那个参考错了。

转载于:https://juejin.im/post/5d01f547518825664b6cdc73

猜你喜欢

转载自blog.csdn.net/weixin_33881140/article/details/93181936
今日推荐