[Java] JDBCドライバもサービスプロバイダーでできてる

2020年3月30日月曜日

Java

前回はサービスプロバイダーを作って見たけど、試しにJDBCドライバを実装してみよう(中身はないけど)。
サービスに相当するものはjava.sql.Driverインターフェースとして用意されているので、これを実装する。

大事なのはスタティックイニシャライザで DriverManager.registerDriver を呼び出して、自分自身を登録すること。
これによって、アプリケーション側が使いたいドライバのクラスを登録する必要がなくなる。

ドライバ

local.provider.MyDriver.java
package local.provider;

import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.DriverPropertyInfo;
import java.sql.SQLException;
import java.util.logging.Logger;
import java.util.Properties;

public class MyDriver implements Driver {
    static {
        try {
            DriverManager.registerDriver(new MyDriver());
        } catch(SQLException e) {
            throw new ExceptionInInitializerError(e);
        }
    }

    @Override public boolean acceptsURL(String url) {
        return true;
    }

    @Override public Connection connect(String url, Properties info) {
        return new MyConnection();
    }

    @Override public int getMajorVersion() {
        return 0;
    }

    @Override public int getMinorVersion() {
        return 0;
    }

    @Override public Logger getParentLogger() {
        return null;
    }

    @Override public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) {
        return new DriverPropertyInfo[] {};
    }

    @Override public boolean jdbcCompliant() {
        return false;
    }
}
java.sql.Connectionを実装したクラス(local.provider.MyConnection)も必要になるけど、中身は空なので省略。

META-INF/services/java.sql.Driver
local.provider.MyDriver
ビルドして、jarを作る。
$ javac -d bin -cp .:../api/local-api.jar src/local/provider/*.java
$ jar cvf local-provider.jar -C bin .

呼び出し側

local/app/MyService.java
package local.app;

import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Collections;
import java.util.Properties;

import local.api.MyService;

public class Main {
    public static void main(String ... args) throws SQLException {
        for (Driver d : Collections.list(DriverManager.getDrivers())) {
            System.out.println(d);
        }

        Connection conn = DriverManager.getConnection("jdbc:mydriver/localhost/db", new Properties());
    }
}
コンパイル
$ javac -d bin -cp . src/local/app/*.java
$ jar cvf local-app.jar -C bin .
実行
$ java -cp ./lib/local-app.jar:./lib/local-provider.jar local.app.Main
local.provider.MyDriver@266474c2

ちなみに

DriverManager.getConnectionはDriverManager.getDriversを呼び出して返ってきたドライバのconnectを呼び出して、最初にnull以外のコネクションを返したドライバを採用しているみたい。
$ tree
.
├── app
│   ├── bin
│   │   └── local
│   │       └── app
│   │           └── Main.class
│   ├── local-app.jar
│   └── src
│       └── local
│           └── app
│               └── Main.java
├── lib
│   ├── local-app.jar
│   └── local-provider.jar
└── provider
    ├── bin
    │   ├── META-INF
    │   │   └── services
    │   │       └── java.sql.Driver
    │   └── local
    │       └── provider
    │           └── MyDriver.class
    ├── local-provider.jar
    └── src
        └── local
            └── provider
                └── MyDriver.java