DriverManager类在加载时,调用此方法等效于
分类:美高梅网上注册平台

maven配置

使用JDBC进行数据库操作的第一步就是驱动注册(当然你得先导入JAR)。驱动注册有多种方式,第一步必然是获得正确的驱动名称与URL格式

使用jdbc方式连接数据库时会使用一句代码Class.forName(String className).这句话是什么意思呢?首先说一点Class.forName(String className)这个方法的作用是装载className这个字符串指定的类。

 <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.41</version> </dependency>

驱动名称与URL格式

RDBMS 驱动程序名称 URL格式
MySQL com.mysql.jdbc.Driver jdbc:mysql://hostname/databaseName
ORACLE oracle.jdbc.driver.OracleDriver jdbc:oracle:thin:@hostname:portNumber:databaseName
PostgreSQL org.postgresql.Driver jdbc:postgresql://hostname:port/dbname
DB2 com.ibm.db2.jdbc.net.DB2Driver jdbc:db2:hostname:port Number/databaseName
Sybase com.sybase.jdbc.SybDriver jdbc:sybase:Tds:hostname: portNumber/databaseName

常用的驱动程序名称与URL格式如上表所示,随着版本变化,会存在些许变动,如有不对,还需重新查验**比如MYSQL中5与6 ,**mysql5用的驱动是com.mysql.jdbc.Driver,mysql6以后用的是com.mysql.cj.jdbc.Driver

官方文档

 

返回与带有给定字符串名的类或接口相关联的 Class 对象。调用此方法等效于:

  Class.forName(className, true, currentLoader)

 其中 currentLoader 表示此类的定义类加载器。

例如,以下代码片段返回 java.lang.Thread 类的运行时 Class 描述符。

   Class t = Class.forName("java.lang.Thread")

 调用 forName("X") 将导致名为 X 的类被初始化。

参数:

className - 所需类的完全限定名。

返回:

具有指定名的类的 Class 对象。

通俗的说就是:获得字符串参数中指定的类,并初始化该类

类装载

style="color: #ff0000; font-size: 18pt;">类装载就是把一个类或是一个接口的字节码文件,通过解析该字节码来构建代表这个类或是这个接口的 style="color: #ff0000; font-size: 18pt;">实例的过程。 
这个字节码文件来源可能是压缩包、网络、运行时编译出的或者自动生成的class文件,jvm spec没有规定必须从什么地方加载。

类装载的两种方式:

1.Class c1 = Class.forName ("java.lang.String"); 

2.ClassLoader cl = new  ClassLoader(); 

Class cl.loadClass( String name, boolean resolve );

 

java代码

MYSQL的驱动下载

MYSQL的connector下载地址 图片 1另外还有完整的文档

两种装载方法的区别:

不同的类装载器

  Class.forName是从指定的classloader中装载类,如果没有指定,也就是一个参数的时候,是从装载 > style="color: #ff0000; font-size: 18pt;">当前对象实例所在的classloader中装载类。

  而ClassLoader的实例调用loadclass方法,是指从当前ClassLoader实例中调用类,而这个实例与装载当前所在类实例的Classloader也许不是同一个. 

      说白了就是他们实现装载的时候,使用的类装载器的指定是不同的。那为什么使用不同的ClassLoader来装载类呢?

      其实使用多个classloader加载类的情况非常常见,比如说我们的app server都是这样的. 在Web与EJB间, 他们的 > style="color: #ff0000; font-size: 14pt;">classLoader就是不同的,这样做的目的就是为了避免两者间类装载的相互干扰。

是否实例化类

      Class的装载分了三个阶段,loading(装载),linking(连接)和initializing(实例化)分别定义在The Java Language Specification的12.2,12.3和12.4。

 Class.forName(className)实际上是调用Class.forName(className,  > style="color: #ff0000; font-size: 18pt;">true, this.getClass().getClassLoader())。注意第二个参数,是指Class被loading后是不是必须 > style="color: #ff0000; font-size: 18pt;">被初始化

    ClassLoader.loadClass(className)实际上调用的是ClassLoader.loadClass(name,  > style="color: #ff0000; font-size: 18pt;">false),第二个参数指出Class是否被 > style="color: #ff0000; font-size: 18pt;">link

    区别就出来了。Class.forName(className)装载的class > style="color: #ff0000; font-size: 18pt;">已经被实例化,而ClassLoader.loadClass(className)装载的class > style="color: #0000ff; font-size: 18pt;">还没有被link,所以就更谈不上实例化了。

  一般情况下,这两个方法效果一样, > style="color: #ff0000; font-size: 18pt;">都能装载Class。但如果程序需要Class被 > style="color: #0000ff; font-size: 18pt;">实例化,就必须用Class.forName(name)了。

例如,在JDBC中加载mysql的驱动类时(关于注册jdbc驱动请参看另外一篇文章,jdbc注册驱动的三种方式),Class.forName("com.mysql.jdbc.Driver"),
如果 > style="color: #ff0000;">换成getClass().getClassLoader().loadClass("com.mysql.jdbc.Driver"),就不行,因为它只是向jvm装载了Driver > style="color: #ff0000; font-size: 18pt;">并没有实例化,就不能执行响应的操作。

 打开com.mysql.jdbc.Driver的源代码看看,

// Register ourselves with the DriverManager
static {
    try {
        java.sql.DriverManager.registerDriver(new Driver());
    } catch (SQLException E) {
        throw new RuntimeException("Can't register driver!");
    }
}

style="background-color: initial;">可以看到,Driver在static块中会注册自己到java.sql.DriverManager。而static块就是在Class的初始化中被执行。所以这个地方就只能用Class.forName(className)。

 

public static void main(String[] args){ Enumeration<Driver> drivers = DriverManager.getDrivers(); Driver driver; while (drivers.hasMoreElements { driver = drivers.nextElement(); System.out.println(driver.getClass() + "------" + driver.getClass().getClassLoader; } System.out.println(DriverManager.class.getClassLoader;}

oracle驱动下载

图片 2

资源

 

1. 

2.Class.forName和ClassLoader.loadClass的比较

 

3.自定义ClassLoader 

 

 4.ClassLoader分析 

 

输出结果如下:

驱动注册

在通过驱动管理器创建连接前需要进行驱动注册 驱动注册有三种形式 Class.forName("com.mysql.jdbc.Driver"); DriverManager.registerDriver(new com.mysql.jdbc.Driver; System.setProperty("jdbc.drivers","com.mysql.jdbc.Driver"); 可以查看mysql-connector中的源码

package com.mysql.jdbc;import java.sql.SQLException;public class Driver extends NonRegisteringDriver implements java.sql.Driver {//// Register ourselves with the DriverManager//static {try {java.sql.DriverManager.registerDriver(new Driver;} catch (SQLException E) {throw new RuntimeException("Can't register driver!");}}/*** Construct a new driver and register it with DriverManager** @throws SQLException* if a database error occurs.*/public Driver() throws SQLException {// Required for Class.forName().newInstance()}}

类加载时,会执行<clinit>方法,在jvm第一次加载class文件时调用,包括静态变量初始化语句和静态块的执行 。所以Class.forName("com.mysql.jdbc.Driver");会调用java.sql.DriverManager.registerDriver(new Driver; 完成了驱动的注册所以直接调用java.sql.DriverManager.registerDriver(new Driver; 也是可以的驱动管理器类加载时,也会从系统属性"jdbc.drivers" 中获取数据 所以,在驱动管理器加载之前,可以使用 System.setProperty("jdbc.drivers","com.mysql.jdbc.Driver"); 进行设置 也可以同时设置多个比如System.setProperty("jdbc.drivers","XXXDriver:XXXDriver:XXXDriver"); 图片 3

  • DriverManager.registerDriver(new com.mysql.jdbc.Driver; 依赖具体,没有面向抽象编程,不推荐
  • System.setProperty("jdbc.drivers","com.mysql.jdbc.Driver"); DriverManager加载前有用,不够灵活方便
  • Class.forName("com.mysql.jdbc.Driver"); 通常使用
class com.mysql.jdbc.Driver------sun.misc.Launcher$AppClassLoader@2a139a55class com.mysql.fabric.jdbc.FabricMySQLDriver------sun.misc.Launcher$AppClassLoader@2a139a55null

自动加载的4.0时代

从JDBC4.0开始,就不在需要主动的显式加载,对应JDK6 在调用 getConnection 方法时,DriverManager 会试着从初始化时加载的那些驱动程序以及使用与当前 applet 或应用程序相同的类加载器显式加载的那些驱动程序中查找合适的驱动程序。 图片 4JDBC 4.0 的规范规定,所有 JDBC 4.0 的驱动 jar 文件必须包含一个 java.sql.Driver,它位于 jar 文件的 META-INF/services 目录下。这个文件里每一行便描述了一个对应的驱动类**在启动项目或是服务时,会判断当前classspath中的所的jar包,并检查他们META-INF目录下,是否包含services文件夹,如果包含,就会将里面的配置加载成相应的服务。**META-INF/services/jdbc.sql.Driver文件内容为需要加载的驱动,如下图所示 图片 5 可以打印出所有的已经加载的驱动程序,如下图所示,与上图文件中的不谋而合 图片 6 所以,对于4.0前,使用Class.forName 4.0后我们只需要将JAR包管理好就足够了,这一切都会有人帮我们完成(当然前提是需要使用配套的驱动Jar包) 完整的loadInitialDrivers方法

private static void loadInitialDrivers() {String drivers;try {//获取环境变量中jdbc.drivers的列表drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {public String run() {return System.getProperty("jdbc.drivers");}});} catch (Exception ex) {drivers = null;}//如果按照规范在jar包中的META-INF/services设置了文件,将会加载为服务// If the driver is packaged as a Service Provider, load it.// Get all the drivers through the classloader// exposed as a java.sql.Driver.class service.// ServiceLoader.load() replaces the sun.misc.Providers()AccessController.doPrivileged(new PrivilegedAction<Void>() {public Void run() {ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);Iterator<Driver> driversIterator = loadedDrivers.iterator();/* Load these drivers, so that they can be instantiated.* It may be the case that the driver class may not be there* i.e. there may be a packaged driver with the service class* as implementation of java.sql.Driver but the actual class* may be missing. In that case a java.util.ServiceConfigurationError* will be thrown at runtime by the VM trying to locate* and load the service.** Adding a try catch block to catch those runtime errors* if driver not available in classpath but it's* packaged as service and that service is there in classpath.*/try{//依次加载所有驱动,访问过后不就加载了嘛while(driversIterator.hasNext {driversIterator.next();}} catch(Throwable t) {// Do nothing}return null;}});println("DriverManager.initialize: jdbc.drivers = " + drivers);//如果环境变量中没有设置的驱动程序,就可以结束了//否则就将环境变量中的驱动程序加载一下if (drivers == null || drivers.equals("")) {return;}String[] driversList = drivers.split(":");println("number of Drivers:" + driversList.length);for (String aDriver : driversList) {try {println("DriverManager.Initialize: loading " + aDriver);Class.forName(aDriver, true,ClassLoader.getSystemClassLoader;} catch (Exception ex) {println("DriverManager.Initialize: load failed: " + ex);}}}

简言之,loadInitialDrivers主要做了两件事情:

  • 读取系统属性jdbc.drivers,如果为空就拉到,如果存在将会进行加载。
  • 检查jar包中的META-INF/services,如果有那么会自动注册,服务注册依赖的是ServiceLoader

对于加载的驱动程序,在前面已经说过,静态代码块中,会完成注册,而注册就是添加到CopyOnWriteArrayList<DriverInfo> registeredDrivers中 图片 7

JDBC SPI是如何初始化的?

总结

驱动注册就是加载数据库的驱动程序,是使用JDBC进行数据库操作的第一步。 JDBC的桥接模式,提供给应用程序开发者的API是抽象,这个驱动程序就是实现,如果不能进行成功注册,就没办法提供后续的服务了,所以驱动注册很重要。 以 JDBC4.0为分水岭 如果按照JDBC4.0后的规范,在jar包中设置了META-INF/services/jdbc.sql.Driver文件,并且写入了驱动,那么会自动加载 如果是之前的,则需要使用Class.forName()进行显式加载。 自动加载依赖ServiceLoader.load(Driver.class);,其中关于目录的设置是写死的PREFIX = "META-INF/services/",想要深入研究可以翻阅ServiceLoader 图片 8 原文地址:JDBC驱动程序注册 JDBC简介

首先,我们看到main方法是调用了DriverManager方法拿到mysql的驱动类的。

public class DriverManager { /** * Load the initial JDBC drivers by checking the System property * jdbc.properties and then use the {@code ServiceLoader} mechanism */ static { loadInitialDrivers(); println("JDBC DriverManager initialized"); }}

DriverManager类在加载时,会调用loadInitialDrivers()方法。

本文由美高梅网上注册平台发布于美高梅网上注册平台,转载请注明出处:DriverManager类在加载时,调用此方法等效于

上一篇:全栈测试、高级测试、测试开发等高等级测试工 下一篇:没有了
猜你喜欢
热门排行
精彩图文