1. 程式人生 > 實用技巧 >SPI在JDBC中的運用

SPI在JDBC中的運用

前言

之前學習了JDK SPI的機制,本文專門討論2個內容:
1.為什麼在使用SPI後,不需要Class.forName()了?
2.SPI在JDBC中的運用。

JDBC模板程式碼

    private static final String URL = "jdbc:mysql://localhost:3306/demo?useSSL=true&useUnicode=true&characterEncoding=UTF-8";
    private static final String DRIVER = "com.mysql.jdbc.Driver";

    //載入驅動資訊,使用SPI之後,不需要下面這行程式碼了。
    static {
        try {
            Class.forName(DRIVER);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) throws Exception {
        Connection conn = DriverManager.getConnection(url, user, password); 
        String sql = "insert into user (name, pwd) values(?,?)";
        Statement st = conn.createStatement();
        st.executeQuery(sql);
    }

為什麼在使用SPI後,不需要Class.forName()了?

首先我們需要了解:Class.forName("com.mysql.jdbc.Driver")的作用是:

根據JVM類載入的原理,該程式碼會將這個位元組碼檔案載入到虛擬機器內部,
而由於類載入共有7個階段,並且Class.forName()方法中使用了JDK反射包中Class<?> caller = Reflection.getCallerClass();
所以,Class.forName()方法不僅僅會載入Driver類,還會執行它的類初始化的過程(即靜態程式碼塊,靜態變數賦值等操作)

其次我們看看為什麼不需要Class.forName()了?

我們來分析一下main方法中的第一句原始碼:Connection conn = DriverManager.getConnection(url, user, password);
首先呼叫該方法前,JVM會載入DriverManager類,然後執行連線,初始化。

DriverManager.Class原始碼
    /**
     * Load the initial JDBC drivers by checking the System property
     * jdbc.properties and then use the {@code ServiceLoader} mechanism
     * 這裡清楚的說明了,該程式碼塊會使用SPI機制進行服務發現。
     */
    static {
        loadInitialDrivers();
        println("JDBC DriverManager initialized");
    }

然後跟蹤程式碼loadInitialDrivers();會發現:

ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator<Driver> driversIterator = loadedDrivers.iterator();

/* 
 * 省略註釋
 */
try{
    while(driversIterator.hasNext()) {
        driversIterator.next();
    }
} catch(Throwable t) {
// Do nothing
}

我的上一篇部落格JDK中的SPI機制中的例1和這裡是一樣的,因為我本來也是參考它來學習的。
根據上一篇部落格裡面的類載入分析,我們知道了這裡DriverManager.Class的靜態初始化,和顯示的執行Class.forName()是一致的。
因為他們使用的類載入器分別是:
1.DriverManager.Class靜態初始化內的SPI機制所使用的是:執行緒上下文類載入器,預設為系統類載入器AppClassLoader。
2.Class.forName()為main方法所在的類的類載入器:系統類載入器AppClassLoader。
所以這裡預設是同一個類載入器來載入我們classpath下面的com.mysql.jdbc.Driver

綜上:
1.在使用SPI後,不需要顯示呼叫Class.forName()了。
2.SPI在JDBC中的運用就是如此,它使用了SPI來對實現方做了約束,並把實現獨立於實現方中,JDK這樣來提供了一種服務提供發現機制.