从JDBC源码带你了解服务提供者框架

工作一年,一直想写博客记录和分享一些想法,做一名分享的Coder,尽力去写一些高质量的博文。希望能够坚持下去,对博文有建议或意见的朋友们可以联系我。

个人邮箱:zhangshaoqiangchn@gmail.com
前言: 首先让我们回忆一下在编写JDBC代码的时候,我们在获取Connection的时候怎么获取的
Class.forName("com.mysql.jdbc.Driver"); Connection connection= DriverManager.getConnection("jdbc:mysql:///mydatabase", "root", "root");

  1. 通过反射的方式将com.mysql.jdbc.Driver的class文件通过类加载器加载到内存中
  2. 通过DriverManager得到Connection
第一步我们通常叫注册驱动,那什么叫做注册驱动?都是通过什么实现的注册驱动呢?下面让我们带着问题去了解服务提供框架。
定义:
多个服务提供者实现一个服务,系统为服务提供者的客户端提供多个实现,并把他们从多个实现中解耦出来。——–《Effective Java》
组件:
  1. 服务接口(Service Interface):提供者去实现的。
  2. 提供者者注册API(provider Registration API):系统用来注册实现的。
  3. 服务访问API(Service Access API):客户端用来获取实例的。
  4. 服务提供者API(Service Provider Interface)(可选):创建其服务实例对象的。
实际场景: sun公司在制定JDBC这套规则的时候,如果MySQL还没有出现,sun公司会再给MySQL重新制定一套规则吗?一定不会吧,可是这个问题怎么解决?
步骤:
1.所以sun公司首先自己定义了一套服务接口,就是这些这些主要步骤的定义。
2.MySQL服务商去实现这套接口。
3.现在调用者想使用MySQL提供的服务,首先进行注册,也就是调用提供者者注册API,将MySQL的实现的服务提供者接口添加到JDBC的注册列表中。
4.注册之后调用者只需要调用 服务访问API,告之我要使用哪一个SQL服务商的实现类,服务访问API就会调用服务提供者接口的实现返回对应的服务实例。
这里要说明一点因为服务的实现都是后于JDBC规则出现的,所以想实例化这些对象只能通过反射进行实例化,按照类名进行注册,所以服务提供者接口 的实现负责创建其服务实现的实例。同时使用服务提供者接口进行注册,对于MySQL而言就是Driver。
第一:让我们来看一下MySQL是怎么实现的服务注册:
com.mysql.jdbc.Driver.java
package com.mysql.jdbc; import com.mysql.jdbc.NonRegisteringDriver; import java.sql.DriverManager; import java.sql.SQLException; 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!"); } } }

java.sql.DriverManager.java
// List of registered JDBC drivers private final static CopyOnWriteArrayList registeredDrivers = new CopyOnWriteArrayList(); public static synchronized void registerDriver(java.sql.Driver driver) throws SQLException { if(driver != null) { registeredDrivers.addIfAbsent(new DriverInfo(driver)); } else { throw new NullPointerException(); } println("registerDriver: " + driver); }

java.sql.Driver就是服务提供者接口,NonRegisteringDriver就是其实现类,com.mysql.jdbc.Driver只是进行注册。在静态代码块中调用了DriverManager.registerDriver方法,并且将实现类添加到JDBC的注册列表(registeredDrivers)。所以在我们通过反射加载com.mysql.jdbc.Driver就可以完成注册了。这也就是文章开头问题的答案。
第二:让我们来看一下客户端是怎么调用服务
1.首先通过getConnection获取
DriverManager.getConnection("jdbc:mysql:///mydatabase", "root", "root");

2.
java.sql.DriverManager.java
public static Connection getConnection(String url) throws SQLException {java.util.Properties info = new java.util.Properties(); // Gets the classloader of the code that called this method, may // be null. ClassLoader callerCL = DriverManager.getCallerClassLoader(); return (getConnection(url, info, callerCL)); }

3.
java.sql.DriverManager.java( 这是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; } } }

重点是遍历registeredDrivers 这就上面注册服务的注册列表
aDriver.driver.connect(url, info);
这个就是服务提供者进行自己实现的方法,如果URL符合提供者自己的规则就返回对应Connection
(MySQL是通过字符串前缀进行匹配的,有兴趣的朋友可以自行查看源码)
总结: 所以对于JDBC来说Connection就是服务接口,DriverManager.registerDriver是提供者注册API,DriverManager.getConnection是服务访问API,Driver是服务提供者接口
【从JDBC源码带你了解服务提供者框架】其实服务提供者框架作用主要是就是隐藏子类实现,而且可以不断的进行扩展,同时将这些服务整合到一起,使调用者察觉不到子类的不同。

    推荐阅读