导读
     通过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. ORM查询总结版

    目录 概要 ORM常用字段 ORM基础 自定义一个插入类型,即固定长度 创建类终极版 多对多关系表创建 常用几个代码 参数 ORM与数据库代码对应的关系 外键使用分表很麻烦,要先删除主表后,再删除 不 ...

  2. OSX 10.14.2 安装Cocoapods 出现问题的解决方法

    今天尝试用 Cocoapods安装个第三方库.. 输入pod install, 发现 command not find. WTF! 估计是升级10.11后Cocoapods被干掉了. 我输入 sudo ...

  3. Windows系统调用中的现场保存

    Windows内核分析索引目录:https://www.cnblogs.com/onetrainee/p/11675224.html Windows系统调用中的现场保存 我们之前介绍过三环进零环的步骤 ...

  4. 帝国cms网站管理系统之安全设置最优化分享

    首先我们来认识一下帝国CMS安全认证特性:帝国登录四重安全验证:第一重:密码双重MD5加密,密码不可破解,假如数据库被下载,也无法获取真实密码.第二重:后台目录自定义,假如对方知道密码也找不到登录后台 ...

  5. 利用 js 的一些函数调用,排序

    编辑器:Sublime Text 3 1.冒泡排序 let arr = new Array(5,9,3,6,7,8,4,2,);bubbleSort(arr);console.log(arr);fun ...

  6. Linux系统基础

    Linux系统基础 目录   简介 0x01 Linux文件与目录管理 0x02 Linux系统用户以及用户组管理 0x03文档的压缩与打包 0x04 apt安装软件 0x05 进程管理 标签 Lin ...

  7. 分布式FastDFS集群部署

    FastDFS FastDFS的作者余庆在其 GitHub 上是这样描述的:"FastDFS is an open source high performance distributed f ...

  8. Python 3 既是激进的又是克制的,这些提议被否决了

    [译]PEP 3099--Python 3 中不会改变的事情 导语: Python 3.8 已经发布了,引进了不少变更点.关于 3.9 预计引入的修改,也披露了一些.我们之前还关注过 GIL 的移除计 ...

  9. 提权案例(一)渗透某asp.net网站通过sql server数据库public 提权 思路分享

    先罗列出sql server 角色用户的权限 按照从最低级别角色(bulkadmin)到最高级别角色(sysadmin)的顺序进行描述:1.bulkadmin:这个角色可以运行BULK INSERT语 ...

  10. 基于 HTML5 + WebGL 实现的垃圾分类系统

    前言 垃圾分类,一般是指按一定规定或标准将垃圾分类储存.分类投放和分类搬运,从而转变成公共资源的一系列活动的总称.分类的目的是提高垃圾的资源价值和经济价值,力争物尽其用.垃圾在分类储存阶段属于公众的私 ...