导读
     通过jdbc链接数据库,是每个学习Java web 方向的人必然一开始会写的代码,虽然现在各路框架都帮大家封装好了jdbc,但是研究一下jdbc链接的套路还是很意义
    术语以及相关类
    SPI Service Provider Interface
    classload 类加载器
     AccessController 安全访问类
 
普通JDBC连接方式(jdk1.8)
①: Connection conn = DriverManager.getConnection(sqlUrl,sqlUserName,sqlPassword);
②: connection.prepareStatement(sql);
点击DriverManager类,你会发现,DriverManager初始化时有一个静态代码块要加载
static {
  loadInitialDrivers();
  println("JDBC DriverManager initialized");
}
loadInitialDrivers 方法的部分
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
  //这里典型的SPI,Java SPI 实际上是“基于接口的编程+策略模式+配置文件”组合实现的动态加载机制
  ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
  Iterator<Driver> driversIterator = loadedDrivers.iterator();
try{
while(driversIterator.hasNext()) {
  driversIterator.next();
}
} catch(Throwable t) {
  // Do nothing
}
  return null;
}
});
jdk早期,数据库驱动如何加载的尼???
Class.forName(""com.mysql.jdbc.Driver"),
Connection conn = DriverManager.getConnection(sqlUrl,sqlUserName,sqlPassword);
先编程式加载MySQL数据库驱动,其次通过mysql驱动连接数据库
这2种方式最明显的不同
以前你需要指定要加载那个数据库驱动,是mysql,oracel还是sqlserver的,现在你只要在maven中引入数据库驱动jar包即可,其他的通过SPI一键搞定,SPI能智能识别到底要调用那个驱动连接数据库,原理很简单,在这个方法中 getConnection(sqlUrl,sqlUserName,sqlPassword);
getConnection 方法部分片段
for(DriverInfo aDriver : registeredDrivers) {
  // If the caller does not have permission to load the driver then
  // skip it.
  if(isDriverAllowed(aDriver.driver, callerCL)) {
  try {
    println(" trying " + aDriver.driver.getClass().getName());
    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());
  }
}
isDriverAllowed(aDriver.driver, callerCL) 方法会遍历所有已在maven中引入的驱动,aDriver.driver.connect(url, info); 方法会调用当期的驱动尝试连接数据库,如果能通过此驱动连接数据库成功,就返回,否则继续尝试(各个数据库库驱动如果无法连接数据库, aDriver.driver.connect(url, info); 此方法会返回null)。至此通过SPI 优雅的加载各大厂商的驱动就实现啦,哈哈
至于SPI 怎么玩,给大家一张图,具体怎么玩百度一下,这里就不多说啦
为啥说 jdbc 加载数据库驱动如何破坏双亲委托模式???
现在我们聊聊这中间涉及到一个隐藏知识点,类加载器,java 有以下几大类加载器
①:BootStrapClassload
②:ExtentionClassload
③:ApplicationClassload
④:自定义类加载器
Java 类加载遵循双亲委托模式,用白话说就是 子类类加载类之前都会咨询父类类加载器,这个类属不属于你的呀,你有加载过吗?如果BootStrapClassload,ExtentionClassload 都说我木有加载过,那么ApplicationClassload才会加载
jdk中具体的具体实现的双亲委托模式的,大家可以参考***.class.getClassLoader().loadClass("");下面的代码就是典型双亲委托模式
protected Class<?> loadClass(String name, boolean resolve)
  throws ClassNotFoundException{
  synchronized (getClassLoadingLock(name)) {
  // First, check if the class has already been loaded
  Class<?> c = findLoadedClass(name);
  if (c == null) {
    long t0 = System.nanoTime();
  try {
  if (parent != null) {
    c = parent.loadClass(name, false);
  } else {
    c = findBootstrapClassOrNull(name);
  }
  } catch (ClassNotFoundException e) {
    // ClassNotFoundException thrown if class not found
    // from the non-null parent class loader
  }
 
  if (c == null) {
    // If still not found, then invoke findClass in order
    // to find the class.
    long t1 = System.nanoTime();
    c = findClass(name);
    // this is the defining class loader; record the stats
    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
    sun.misc.PerfCounter.getFindClasses().increment();
  }
}
  if (resolve) {
    resolveClass(c);
  }
    return c;
  }
}
可能有人问,扯了这么多,跟我们上述JDBC加载驱动有啥关系?
我很肯定的回答有的哈哈
Java 类加载器,其实就是分模块加载资源?什么意思尼
 
Bootstrap ClassLoader :最顶层的加载类,主要加载核心类库,也就是我们环境变量下面%JRE_HOME%\lib下的rt.jar、resources.jar、charsets.jar和class等
Extention ClassLoader :扩展的类加载器,加载目录%JRE_HOME%\lib\ext目录下的jar包和class文件
Appclass Loader: 加载当前应用的classpath的所有类。
强调一下在目前双亲委托模式下,Bootstrap ClassLoader 不可以夸模块加载Appclass Loader。Appclass Loader也不可以夸模块加载Bootstrap ClassLoader,原因很简单,他们在自己的模块路径下中找不到要加载类(比如:com.mysql.jdbc.Driver 这个类,它的类路径是在classpath下,你在rt.jar下下,你怎么找到这个类)
 
那么问题来啦,DriverManager 这个类属于rt.jar 由Bootstrap ClassLoader 来加载的,各大驱动jar包是在classpath下由Appclass Loader 加载的,听着没事问题哈???
但是,你们仔细看一下代码,你们会发现DriverManager.getConnection(sqlUrl,sqlUserName,sqlPassword);在这个方法中已经完成驱动类的初始化啦,那么就说明这个驱动类是由Bootstrap ClassLoader 加载的???
这是怎么回事尼???
 
这里就涉及到了另外一个名词上下文类加载器,对应代码
Thread.currentThread().getContextClassLoader();
ClassLoader.getSystemClassLoader();
这2个方法在普通Java 项目下是相同的,但是在web应用中是不同的,各位有兴趣可以去查一下 
每个线程都有自己的线程上下文类加载器,并且每个线程的上下文类记载器都是父线程的类加载器,那么我只要考虑第一个线程类加载器就可以啦,因为他是所有的线程的父类,那么对于普通Java程序,谁是第一个线程,这个大家肯定知道 Java 的 Main 方法哈哈,那么main 方法的上下文来加载器是谁尼?? 我们去看一下,main方法的入口类 Launcher 类
看一下 Launcher 类 初始化
public Launcher() {
Launcher.ExtClassLoader var1;
try {
  var1 = Launcher.ExtClassLoader.getExtClassLoader();
} catch (IOException var10) {
  throw new InternalError("Could not create extension class loader", var10);
}
 
try {
  this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
} catch (IOException var9) {
  throw new InternalError("Could not create application class loader", var9);
}
// 看这里,原来第一个上下文线程类加载器的是AppClassLoader
  Thread.currentThread().setContextClassLoader(this.loader);
  String var2 = System.getProperty("java.security.manager");
if(var2 != null) {
  SecurityManager var3 = null;
if(!"".equals(var2) && !"default".equals(var2)) {
try {
  var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();
} catch (IllegalAccessException var5) {
;
} catch (InstantiationException var6) {
;
} catch (ClassNotFoundException var7) {
;
} catch (ClassCastException var8) {
;
}
} else {
  var3 = new SecurityManager();
}
if(var3 == null) {
  throw new InternalError("Could not create SecurityManager: " + var2);
}
  System.setSecurityManager(var3);
}
}
 
在看一下通过Java SPI 技术加载数据库驱动的时候,他是直接获取线程上下文类加载器加载数据库驱动(直接通过ApplicationClassload 来加载驱动的),
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
load源码
public static <S> ServiceLoader<S> load(Class<S> service) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return ServiceLoader.load(service, cl);
}
.
BootStrapClassload偷偷滴指派Appclassload 说帮我加载一下驱动类吧,本来只能由Appclassload委托BootStrapClassload来加载对应的类哈哈, 所以说,JDBC数据库加载破坏了双亲委托模式
 
结束语
双亲委托模式不是固定死的,它就是Java指定的一个类加载的一个规则,你完全可以不遵守它,只要你能帮类加载到JVM内存中即可。

jdbc 加载数据库驱动如何破坏双亲委托模式的更多相关文章

  1. JDBC 学习笔记(四)—— JDBC 加载数据库驱动,获取数据库连接

    1. 加载数据库驱动 通常来说,JDBC 使用 Class 类的 forName() 静态方法来加载驱动,需要输入数据库驱动代表的字符串. 例如: 加载 MySQL 驱动: Class.forName ...

  2. JDBC加载数据库驱动的方式

    JDBC作为数据库访问的规范接口,其中只是定义一些接口.具体的实现是由各个数据库厂商来完成. 一.重要的接口: 1.public interface Driver 每个驱动程序类必须实现的接口.Jav ...

  3. JDBC:加载数据库驱动、连接数据库(详细讲解)

    加载数据库驱动: 1)由于Java是一个纯面向对象语言,任何事物在其中都必须抽象成类或者类对象,数据库也不例外,JDBC同样也把数据库抽象成面向对象的结构: 2)JDBC将整个数据库驱动器在底层抽象成 ...

  4. java 加载数据库驱动

    JDBC编程步骤见 JDBC编程步骤 JDBC编程的第一步是加载数据库驱动,使用Class类的forName()方法,Class.forName("com.mysql.jdbc.Driver ...

  5. ThinkCMF项目部署出现无法加载数据库驱动解决方案

    最近有个TP项目刚从从本地部署到阿里云服务器上,出现了无法加载数据库驱动的错误,提示 :( 无法加载数据库驱动: Think\Db\Driver 这里分享一下出现该错误的解决步骤: 首先记得项目部署到 ...

  6. JAVA基础|从Class.forName初始化数据库到SPI破坏双亲委托机制

    代码托管在:https://github.com/fabe2ry/classloaderDemo 初始化数据库 如果你写过操作数据库的程序的话,可能会注意,有的代码会在程序的开头,有Class.for ...

  7. springboot启动不能加载数据库驱动Failed to determine a suitable driver class

    SLF4J: Class path contains multiple SLF4J bindings. SLF4J: Found binding in [jar:file:/G:/sharp/repo ...

  8. Java-加载数据库驱动,取得数据库连接

    在Java中想要进行数据库操作,最重要的两个步骤就是加载数据驱动,然后取得数据库连接. 1.加载 数据库驱动( Class.forName(String className) ): 因为Java是一种 ...

  9. 破坏双亲委托机制的一些情况---Tomcat和JDBC,破坏后的安全问题

    采用双亲委托机制的原因 类加载器就是将字节码搬进jvm方法区的组件.我们知道,JVM识别加载进来的类是通过类加载器+类全名完成的,也就是说同一个类由不同类加载器加载进去的话就会被视为不同的类.jdk提 ...

随机推荐

  1. python编程基础之四

    注释: 单行注释 #    例: # age = 10 多行注释  三引号“”“  ”“”,‘‘‘ ’’’ 例:“““  age = 10   ””” 只要注释较难的代码, 注释比例大概占总数的30% ...

  2. markdown下载、安装、破解、汉化与常用语法

    markdown是一种纯文本格式的标记语言.通过简单的标记语法,它可以使普通文本内容具有一定的格式. 一.markdown下载 markdown破解版下载地址: http://www.pc6.com/ ...

  3. Oracle VM VirtualBOX桥接网卡

    1.在VirtualBOX设置中的网络菜单,连接方式选择桥接网卡 2.启动系统,设置静态IP sudo vim /etc/network/interfaces auto enp0s3         ...

  4. Java基础-开篇

    之前在新浪博客写了不少springmvc的相关技术,但新浪博客毕竟不是专业的技术博客,添加代码很不方便,就开始在博客园试试了. 使用java开发也不少年了,准备再次整理一些java基础知识,当然,这次 ...

  5. java位运算,逻辑运算符

    位运算逻辑运算符包括: 与(&),非(~),或(|),异或(^). &:  条件1&条件2  ,只有条件1和条件2都满足, 整个表达式才为真true,  只要有1个为false ...

  6. p0wnedshell的介绍与使用

    0x01 前言 p0wnedShell是一个用c#编写的攻击性PowerShell主机应用程序,它不依赖于PowerShell .exe,而是在PowerShell runspace环境(. net) ...

  7. 后门免杀工具-Backdoor-factory

    水一水最近玩的工具 弄dll注入的时候用到的 介绍这款老工具 免杀效果一般..但是简单实用  目录: 0x01 backdoor-factory简介 0x02 特点功能 0x03 具体参数使用 PS: ...

  8. 事业单位招聘网搭建思路和seo方法

    期望目标 自动采集事业单位招聘信息+自动发布到网站+自动提交网址到百度+自动发外链. 技术框架 前端用layUI,后端用flask,数据库用的elasticsearch,编程语言用Python 3.7 ...

  9. 百万年薪python之路 -- socket()模块的用法

    socket()模块的用法: import socket socket.socket(socket_family,socket_type,protocal=0) socket_family 可以是 A ...

  10. 轻量级CNN模型mobilenet v1

    mobilenet v1 论文解读 论文地址:https://arxiv.org/abs/1704.04861 核心思想就是通过depthwise conv替代普通conv. 有关depthwise ...