java8如何加载驱动类,关于类加载器classloader

java中所有的类都是通过classloader加载的,classloader可以为java程序提供很好的动态特性,深入了解非常有必要。

1)classloader的分类

a4c26d1e5885305701be709a3d33442f.png

从上图看到classloader默认有3类,

BootstrapLoader

ExtClassLoader

AppClassLoader

同时你也可以定制自己的classloader

这些classloader分别加载不同路径下的类,路径如上图所示

2)父子关系的含义

如上图所示,这几个classloader存在父子关系

下层的classloader会委托上层的classloader加载某个类,如果上层找不到才轮到自己,也就是说下层classloader

能看到上层的类,但反过来不是这样,这也成为委托模型

3)父子关系是如何确定的?

默认情况下,BootstrapLoader,ExtClassLoader,AppClassLoader的父子关系是固定,如果你定义了自己的classloader,也许会有多级,那么自定义classloader的初始化的所在的类就默认成为parent

比如你会在AppClassLoader中定义类加载器,此时你定义的classloader的parent就是AppClassLoader

4)类与它所依赖的类的classloader机制

如果一个类是由某个classloader加载,那么这个类依赖的类(非显式的通过某个classloader加载)必须也由该classloader或其父classloader加载,无视子classloader

比如A类依赖B类,你在A类的某个方法中new B(); 不可能A类由 BootstrapLoader加载,而B 类由

AppClassLoader

或者一个A类实现了C 接口,不可能A类由 BootstrapLoader加载,而C接口由 AppClassLoader

5)位于父classloader中的类真的无法依赖位于子classloader中的类码?

通过显式的使用classloader!

5.1)通过URLClassLoader.load

比如在位于AppClassLoader中的Test类中的main方法成功依赖了位于子classloader中Word类

URL u = new URL("file:/tmp/");

URLClassLoader ucl = new URLClassLoader(new URL[] { u });

Class c = ucl.loadClass("org.toy.classloader.Word");

Assembly asm = (Assembly) c.newInstance();

c.start();

5.2)通过thread.getContextClassloader

thread.getContextClassloader默认返回AppClassLoader,除非你显式setContextClassloader

上面的用法不过优雅,让我们从jdk jdbc的源码中学点经验

看看java是如何加载jdbc driver的

view plaincopy to clipboardprint?

java.sql.DriverManager.getConnection----> java.sql.DriverManager.initialize----> java.sql.DriverManager.loadInitialDrivers----> java.security.AccessController.doPrivileged----> java.sql.DriverService.run----> sun.misc.Service.providers(java.sql.Driver.class)----> sun.misc.Service$LazyIterator()----> sun.misc.Service$LazyIterator.next----> Class.forName(cn, true,

loader).newInstance(); java.sql.DriverManager.getConnection---->

java.sql.DriverManager.initialize---->

java.sql.DriverManager.loadInitialDrivers---->

java.security.AccessController.doPrivileged---->

java.sql.DriverService.run---->

sun.misc.Service.providers(java.sql.Driver.class)---->

sun.misc.Service$LazyIterator()---->

sun.misc.Service$LazyIterator.next---->

Class.forName(cn, true, loader).newInstance();

上面的loader就是通过

Thread.currentThread().getContextClassLoader()传递的,在加载之前,上面的next方法,通过classloader.getResources("META-INF/services/java.sql.Driver")

发现文件

比如

jar:file:/home/pwlazy/dev/jdk1.6.0_17/jre/lib/resources.jar!/META-INF/services/java.sql.Driver

jar:file:/home/pwlazy/.m2/repository/mysql/mysql-connector-java/5.1.10/mysql-connector-java-5.1.10.jar!/META-INF/services/java.sql.Driver

这两个jar包都含有这个文件,文件的内容就是驱动的类名

比如上面两个文件分别对应类名sun.jdbc.odbc.JdbcOdbcDriver和

com.mysql.jdbc.Driver

于是classloader开始加载驱动类,

但最后开始开始连接数据库的时候,

会通过下面的方法来验证调用者是否有权限加载驱动类。

view plaincopy to clipboardprint?

private static Class getCallerClass(ClassLoader

callerClassLoader, String driverClassName) { Class

callerC = null; try

{ callerC = Class.forName(driverClassName, true,

callerClassLoader); } catch

(Exception ex) { callerC =

null; // being very careful } return

callerC; } private static Class getCallerClass(ClassLoader

callerClassLoader,

String driverClassName) {

Class

callerC = null;

try {

callerC = Class.forName(driverClassName, true,

callerClassLoader);

}

catch

(Exception ex) {

callerC =

null; // being very careful

}

return

callerC;

}

做个小实验:

如果把调用类放在ExtClassLoader下,那么com.mysql.jdbc.Driver最终是无法调用的,因为调用类处于ExtClassLoader,而com.mysql.jdbc.Driver处于AppClassLoader,所以验证无法通过

但sun.jdbc.odbc.JdbcOdbcDrive是可以,因为它是BootstrapLoader加载的

从上面的分析,我们可以嗅到一个有趣的模式,某个框架制定某个API,而这些api的实现是有其他供应商来提供,为了能让框架类(处于较高层次的classloader)使用api的实现(处于较低层次的classloader)

通过thread.getContextClassloader是来传递classloader(有时候需要thread.setContextClassloader设置好api实现的classloader),用此classloader.getResources找出所有的api实现的具体类名,再用classloader加载之,此时框架都不需要知道api的实现类的类名就能加载之,程序显示了良好的动态性和可扩展性。

总之classloader默认的委托模型使得处于上层的classloader中的类无法访问处于下层classloader中的类,这带来了安全性的同时也失去了灵活性,幸好可以使用Thread.currentThread().getContextClassLoader()

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值