Java SPI机制分析,linux操作系统和arm架构

org.postgresql

postgresql

42.2.2

com.microsoft.sqlserver

mssql-jdbc

7.0.0.jre8

分别准备了mysql,postgresql和sqlserver,可以打开jar,发现每个jar包的META-INF/services/都存在一个java.sql.Driver文件,文件里面存在一个或多个类名,比如mysql:

com.mysql.jdbc.Driver

com.mysql.fabric.jdbc.FabricMySQLDriver

提供的每个驱动类占据一行,解析的时候会按行读取,具体使用哪个会根据url来决定;

2.简单实例

String url = “jdbc:mysql://localhost:3306/db3”;

String username = “root”;

String password = “root”;

String sql = “update travelrecord set name=‘bbb’ where id=1”;

Connection con = DriverManager.getConnection(url, username, password);

类路径下存在多个驱动包,具体在使用DriverManager.getConnection应该使用哪个驱动类会解析url来识别,不同的数据库有不同的url前缀;

3.驱动类加载分析

具体META-INF/services/下的驱动类是什么时候加载的,DriverManager有一个静态代码块:

static {

loadInitialDrivers();

println(“JDBC DriverManager initialized”);

}

private static void loadInitialDrivers() {

String drivers;

try {

drivers = AccessController.doPrivileged(new PrivilegedAction() {

public String run() {

return System.getProperty(“jdbc.drivers”);

}

});

} catch (Exception ex) {

drivers = null;

}

// 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() {

public Void run() {

ServiceLoader loadedDrivers = ServiceLoader.load(Driver.class);

Iterator 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);

}

}

}

在加载DriverManager类的时候会执行loadInitialDrivers方法,方法内通过了两种加载驱动类的方式,分别是:使用系统变量方式和ServiceLoader加载方式;系统变量方式其实就是在变量jdbc.drivers中配置好驱动类,然后使用Class.forName进行加载;下面重点看一下ServiceLoader方式,此处调用了load方法但是并没有真正去加载驱动类,而是返回了一个LazyIterator,后面的代码就是循环变量迭代器:

private static final String PREFIX = “META-INF/services/”;

private class LazyIterator

implements Iterator

{

Class service;

ClassLoader loader;

Enumeration configs = null;

Iterator pending = null;

String nextName = null;

private LazyIterator(Class service, ClassLoader loader) {

this.service = service;

this.loader = loader;

}

private boolean hasNextService() {

if (nextName != null) {

return true;

}

if (configs == null) {

try {

String fullName = PREFIX + service.getName();

if (loader == null)

configs = ClassLoader.getSystemResources(fullName);

else

configs = loader.getResources(fullName);

} catch (IOException x) {

fail(service, “Error locating configuration files”, x);

}

}

while ((pending == null) || !pending.hasNext()) {

if (!configs.hasMoreElements()) {

return false;

}

pending = parse(service, configs.nextElement());

}

nextName = pending.next();

return true;

}

private S nextService() {

if (!hasNextService())

throw new NoSuchElementException();

String cn = nextName;

nextName = null;

Class c = null;

try {

c = Class.forName(cn, false, loader);

} catch (ClassNotFoundException x) {

fail(service,

“Provider " + cn + " not found”);

}

if (!service.isAssignableFrom©) {

fail(service,

“Provider " + cn + " not a subtype”);

}

try {

S p = service.cast(c.newInstance());

providers.put(cn, p);

return p;

} catch (Throwable x) {

fail(service,

“Provider " + cn + " could not be instantiated”,

x);

}

throw new Error(); // This cannot happen

}

}

类中指定了一个静态常量PREFIX = “META-INF/services/”,然后和java.sql.Driver拼接组成了fullName,然后通过类加载器去获取所有类路径下java.sql.Driver文件,获取之后存放在configs中,里面的每个元素对应一个文件,每个文件中可能会存在多个驱动类,所以使用pending用来存放每个文件中的驱动信息,获取驱动信息之后在nextService中使用Class.forName加载类信息,并且指定不进行初始化;同时在下面使用newInstance对驱动类进行了实例化操作;每个驱动类中都提供了一个静态注册代码块,比如mysql:

static {

try {

java.sql.DriverManager.registerDriver(new Driver());

} catch (SQLException E) {

throw new RuntimeException(“Can’t register driver!”);

}

}

这里又实例化了一个驱动类,同时注册到DriverManager;接下来就是调用DriverManager的getConnection方法,代码如下:

private static Connection getConnection(

String url, java.util.Properties info, Class caller) throws SQLException

【一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义】

浏览器打开:qq.cn.hn/FTf 免费领取

{

/*

  • When callerCl is null, we should check the application’s

  • (which is invoking this class indirectly)

  • classloader, so that the JDBC driver class outside rt.jar

  • can be loaded from here.

*/

ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;

synchronized(DriverManager.class) {

// synchronize loading of the correct classloader.

if (callerCL == null) {

callerCL = Thread.currentThread().getContextClassLoader();

}

}

你可能感兴趣的:(程序员,面试,java,后端)