zoukankan      html  css  js  c++  java
  • 关于JAVA的SPI机制,JDBC实例

    一、SPI是什么?

      SPI 的全称为 (Service Provider Interface),是 JDK 内置的一种服务提供发现机制。比如JAVA中定义了jdbc的规范,然后由不同的厂商去落地实现该规范(也就是服务),然后可以通过ServiceLoader去加载这些服务。通过SPI机制可以实现模块插拔,比如当前使用的数据库是oracle,如果想切换为MySQL的话只需要将对应的jar包切换为MySQL的即可。(当然对应的sql语句可能会出问题,除非使用的是Hibernate这种不依赖具体关系型数据库的框架)

    二、自己实现一个SPI

      1.定义接口(规范):

    package com.xiezy.spi;
    public interface SPITest {
        public String say(String str);
    }
    

      2.实现接口(服务)

    package com.xiezy.spi;
    public class SPITestImpl implements SPITest {
        @Override
        public String say(String str) {
            return "SPI---" + str;
        }
    }
    

      3.在服务的META-INF/Services目录下创建一个名称为接口全限定名,内容为实现类的全限定名。在本例中名称为:com.xiezy.spi.SPITest

    com.xiezy.spi.SPITestImpl
    

      4.测试类

    package com.xiezy.spi;
    
    import java.util.ServiceLoader;
    
    public class SPITestMain {
        public static void main(String[] args) {
            ServiceLoader<SPITest> spiTests = ServiceLoader.load(SPITest.class);
            for (SPITest spiTest : spiTests) {
                System.out.println(spiTest.say("haha~"));
            }
        }
    }
    

      

    三、实际例子(JDBC)

      相信大家在刚学习jdbc时的使用步骤都是先通过Class.forName("dirver")将对应的Driver加载进JVM才可以使用,但是现在是不需要的。现在我们可以像下面直接使用:

      这是因为SPI机制已经将需要的Driver自动加载了,可以看一下DriverManager.getConnection的源码(只截取重要部分):

    private static Connection getConnection(
            String url, java.util.Properties info, Class<?> caller) throws SQLException {
            /**
            ...
            **/
            //遍历已经注册的Dirver,并尝试去获取连接
            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());
                }
            }
        }    
    

      可以看到是遍历registeredDrivers,如何去获取连接。那么这个registeredDrivers是什么时候赋值的呢?通过源码可以看到有如下方法:

    public static synchronized void registerDriver(java.sql.Driver driver)
            throws SQLException {
    
            registerDriver(driver, null);
        }
    

      但是没看到在什么地方调用了该方法,在了解该方法究竟在哪调用前。我们先看一下DriverManager的static块

       /**
         * 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");
        }
    
    
        private static void loadInitialDrivers() {
            /**
              ...
            **/
            public Void run() {
                //通过SPI机制加载Driver
                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;
             /**
              ...
            **/
    }    
    

      可以看到DriverManager在初始化的时候去加载了Driver的实现类,比如本例子中使用的MySql包

       我们在看一下com.mysql.cj.jdbc.Driver初始化过程

    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!");
            }
        }
     }
    

      可以看到在初始化的时候调用了registeredDrivers方法,所以我们知道了DriverManager中的registeredDrivers属性是在这时候赋值的。

  • 相关阅读:
    使用java写一个小白计算器
    UVALive 6911 Double Swords (Set,贪心,求区间交集)
    UVALive 6910 Cutting Tree(并查集应用)
    Gym 101102C Bored Judge(set--结构体集合)
    有序链表和顺序表
    Gym 101102B The Little Match Girl(贪心+规律)
    UVALive 7070 The E-pang Palace(暴力)
    数据库系统实现 第二章 数据存储
    数据库系统实现 第一章 DBMS实现概述
    数据库系统实现 第六章 查询执行
  • 原文地址:https://www.cnblogs.com/xiezy/p/13094779.html
Copyright © 2011-2022 走看看