聊聊ClassLoader与jdbc的关系(contextClassLoader)
来源:互联网 发布:锋芒网络剧 编辑:程序博客网 时间:2024/05/22 08:10
背景
在前面聊到ClassLoader是如何工作的,有些时候ClassLoader的双亲委托机制不能完成一些特定的类加载任务,比如java提供一些SPI,由厂商来进行具体的实现,比如jdbc,各个数据库厂商根据java提供的SPI来实现各自数据库的连接;这些SPI都定义在核心类里,由bootstrap ClassLoader加载,而在SPI 接口中的代码经常需要加载具体的实现类,但厂商的具体实现又不能由bootstrap ClassLoader加载,那如何实现的呢?答案是contextClassLoader(线程上下文类加载器),下面来以jdbc为例看看到底是如何工作的
实现
先来看一小段jdbc获取连接的代码
Class.forName("com.mysql.jdbc.Driver"); DriverManager.getConnection("jdbc:mysql://localhost:3306/db", "user", "psw");
深挖一下上面的代码,首先Class.forName方法都知道是加载指定的类
- 那么就再看看forName方法吧
public static Class<?> forName(String className) throws ClassNotFoundException { //通过Reflection.getCallerClass()方法获取到调用forName的class Class<?> caller = Reflection.getCallerClass(); //调用forName0,传入的ClassLoader为加载caller的ClassLoader,forName0为native方法 return forName0(className, true, ClassLoader.getClassLoader(caller), caller);}
- 再看看加载com.mysql.jdbc.Driver的时候会发生什么
public class Driver extends NonRegisteringDriver implements java.sql.Driver { public Driver() throws SQLException { } static { try { DriverManager.registerDriver(new Driver()); } catch (SQLException var1) { throw new RuntimeException("Can\'t register driver!"); } }}
注意到这里有个静态代码块,而class的forName方法默认对加载的类进行了链接操作,所以这里的静态代码块会被执行;静态代码块将com.mysql.jdbc.Driver注册到DriverManager,其实就是被DriverManager的静态成员registeredDrivers持有缓存起来,而DriverManager也在这个时候被加载了,根据ClassLoader的双亲委托机制,DriverManager由bootstrap ClassLoader加载;到这里class的forName方法就执行完了
- 下面执行getConnection(获取数据库连接)
public static Connection getConnection(String url, String user, String password) throws SQLException { //将用户名和密码封装到properties里 java.util.Properties info = new java.util.Properties(); if (user != null) { info.put("user", user); } if (password != null) { info.put("password", password); } //调用真正的获取连接的方法,指定caller的class为调用当前方法的类 return (getConnection(url, info, Reflection.getCallerClass()));}private static Connection getConnection( String url, java.util.Properties info, Class<?> caller) throws SQLException { //如果caller存在,则将ClassLoader设置为加载caller的ClassLoader ClassLoader callerCL = caller != null ? caller.getClassLoader() : null; synchronized(DriverManager.class) { //如果ClassLoader不存在,那么ClassLoader设置为当前线程上下文的类加载器(getContextClassLoader)为ClassLoader if (callerCL == null) { callerCL = Thread.currentThread().getContextClassLoader(); } } if(url == null) { throw new SQLException("The url cannot be null", "08001"); } println("DriverManager.getConnection(\"" + url + "\")"); SQLException reason = null; //循环注册了的Driver for(DriverInfo aDriver : registeredDrivers) { //如果callerClassLoader有权限加载aDriver.driver,即:判断两个driver是否由同一个ClassLoader加载的 if(isDriverAllowed(aDriver.driver, callerCL)) { try { println(" trying " + aDriver.driver.getClass().getName()); //通过之前注册的drive获取数据库连接 Connection con = aDriver.driver.connect(url, info); if (con != null) { // Success! println("getConnection returning " + aDriver.driver.getClass().getName()); return (con); } } catch (SQLException ex) { if (reason == null) { reason = ex; } } } else { println(" skipping: " + aDriver.getClass().getName()); } } if (reason != null) { println("getConnection failed: " + reason); throw reason; } throw new SQLException("No suitable driver found for "+ url, "08001");}
小结
SPI加载实现类主要是通过Reflection.getCallerClass()和Thread.currentThread().getContextClassLoader()来避免双亲委托带来的尴尬;
阅读全文
0 0
- 聊聊ClassLoader与jdbc的关系(contextClassLoader)
- contextclassLoader--JDBC
- Java环境变量配置与ClassLoader的关系
- 聊聊ClassLoader
- Java类加载机制ClassLoader之ContextClassLoader
- ContextClassLoader的意义
- java-ContextClassLoader的意义
- ClassLoader与Tomcat的ClassLoader
- ContextClassLoader
- 以jdbc为例搞清contextClassLoader
- 聊聊JDBC
- JDBC与Java的数据类型映射关系
- java类型与jdbc的对应关系
- 关于 ClassLoader.loadClass() 与 Class.forName() 关系
- Java 类加载体系与ContextClassLoader
- Java 类加载体系与ContextClassLoader
- Java 类加载体系与ContextClassLoader
- Java 类加载体系与ContextClassLoader
- 如何通过stm32驱动电源检测芯片cs5463
- 流媒体传输控制协议(RTSP RTP SDP)详解之——sdp
- SQL中的case when then else end用法
- POJ 2391 Ombrophobic Bovines 最大流+二分
- 你看不惯的千禧一代,马云和马化腾都要捧着!
- 聊聊ClassLoader与jdbc的关系(contextClassLoader)
- commons-fileupload实现文件上传功能实例
- Linux下查看、关闭及开启防火墙命令
- 第十章 时间序列(中)
- 处理IOT中纷繁的数据与消息_从未如此简单
- Ubuntu 16.04 安装Python-OpenCV 3.2
- Linux 下如何处理包含空格和特殊字符的文件名
- 电脑配置
- 简单选择排序 C++