dubboからjdbcとspiへ
詳細
最近Dubboソースコードを見ていますが、dubboの機能は拡張点(Extension)に基づいているので、どのモジュールを修正したいのか、簡単に拡張置き換えができます.
このような拡張点は参考になるspiの思想であるが、dubboはjdk原生のserviceLoaderを使用するのではなく、拡張点をロードするためにExtensionLoaderを実現し、キー値ペアをサポートし、より柔軟で、従う規範は基本的に同じである.これは余談です.
SPIとは?SPIは何ができますか?ここに紹介記事があります--リンク
最初にSPIに触れたときは戸惑いましたが、資料を調べてみるとjdbcをSPIの典型的な例として持っている文章が多いことがわかりました.
私が大学に入ったばかりの頃、hibernate、mybatisのようなフレームワークはまだ火がつかず、jdbcリンクデータベースのコードを自分で書くしかなかったことを思い出します.
このClass.forName(「com.mysql.jdbc.Driver」)は、データベースドライバクラスcom.mysql.jdbc.Driverをロードする役割を果たします.コードは次のとおりです.
ロード時に上記のコードの静的コードブロックを先に実行し、
com.mysql.jdbc.driver newは独自のインスタンスを作成し、Driver Managerに登録します.これにより、Driver Managerはドライバを使用してデータベース接続を取得できます.
ここまで、駆動はすでにロード済みで、SPIに関するアプリケーションは何もありません.私も霧水で、SPIがjdbcと何の関係があるのか分かりません.しかし、jdbcがspiを使用している場合、DriverManagerでは必ず対応する実装があり、DriverManagerコードを引き続き見てみると、静的ブロックが以下のようになっています.
loadInitialDrivers()メソッドコードは次のとおりです.
このメソッドでは、ドライバファイルをロードするためにServiceLoaderが呼び出されていることに気づきました.
ようやくSPIの姿が見つかり、見続けるとドライブクラスのロードが遍歴している間に行われていることがわかります
loadedDrivers.iterator()は
LazyIteratorはServiceLoaderの内部プライベートクラスであり、teratorインタフェースを実現している.コードは以下の通りである.
nextServiceはClass.forNameでドライバクラスをロードし、ドライバクラスの静的ブロックを実行し、Driver Managerでインスタンスを登録していることがわかります...では、DriverManagerがクラスspiのメカニズムを使用してすべての駆動クラスを自動的にロードする以上、コードを書くときに行く必要はありません.
はい、このコードはもう余計です.削除しても正常にデータベース接続を確立できます.
最近Dubboソースコードを見ていますが、dubboの機能は拡張点(Extension)に基づいているので、どのモジュールを修正したいのか、簡単に拡張置き換えができます.
このような拡張点は参考になるspiの思想であるが、dubboはjdk原生のserviceLoaderを使用するのではなく、拡張点をロードするためにExtensionLoaderを実現し、キー値ペアをサポートし、より柔軟で、従う規範は基本的に同じである.これは余談です.
SPIとは?SPIは何ができますか?ここに紹介記事があります--リンク
最初にSPIに触れたときは戸惑いましたが、資料を調べてみるとjdbcをSPIの典型的な例として持っている文章が多いことがわかりました.
私が大学に入ったばかりの頃、hibernate、mybatisのようなフレームワークはまだ火がつかず、jdbcリンクデータベースのコードを自分で書くしかなかったことを思い出します.
try {
Class.forName("com.mysql.jdbc.Driver");//1
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/EScore", "root", "root");//2
pst = conn.prepareStatement("SELECT COUNT(1) FROM score");
ResultSet resultSet = pst.executeQuery();
resultSet.next();
System.out.println(resultSet.getInt(1));
} catch (Exception e) {
e.printStackTrace();
}
このClass.forName(「com.mysql.jdbc.Driver」)は、データベースドライバクラスcom.mysql.jdbc.Driverをロードする役割を果たします.コードは次のとおりです.
package com.mysql.jdbc;
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!");
}
}
}
ロード時に上記のコードの静的コードブロックを先に実行し、
DriverManager.registerDriver(new Driver());
com.mysql.jdbc.driver newは独自のインスタンスを作成し、Driver Managerに登録します.これにより、Driver Managerはドライバを使用してデータベース接続を取得できます.
ここまで、駆動はすでにロード済みで、SPIに関するアプリケーションは何もありません.私も霧水で、SPIがjdbcと何の関係があるのか分かりません.しかし、jdbcがspiを使用している場合、DriverManagerでは必ず対応する実装があり、DriverManagerコードを引き続き見てみると、静的ブロックが以下のようになっています.
static {
loadInitialDrivers();
println("JDBC DriverManager initialized");
}
loadInitialDrivers()メソッドコードは次のとおりです.
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;
}
AccessController.doPrivileged(new PrivilegedAction() {
public Void run() {
ServiceLoader loadedDrivers = ServiceLoader.load(Driver.class);
Iterator driversIterator = loadedDrivers.iterator();
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);
}
}
}
このメソッドでは、ドライバファイルをロードするためにServiceLoaderが呼び出されていることに気づきました.
ServiceLoader loadedDrivers = ServiceLoader.load(Driver.class);
ようやくSPIの姿が見つかり、見続けるとドライブクラスのロードが遍歴している間に行われていることがわかります
Iterator driversIterator = loadedDrivers.iterator();
try{
while(driversIterator.hasNext()) {
driversIterator.next();
}
} catch(Throwable t) {
// Do nothing
}
loadedDrivers.iterator()は
private LazyIterator lookupIterator;
LazyIteratorはServiceLoaderの内部プライベートクラスであり、teratorインタフェースを実現している.コードは以下の通りである.
// Private inner class implementing fully-lazy provider lookup
//
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(c)) {
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
}
public boolean hasNext() {
if (acc == null) {
return hasNextService();
} else {
PrivilegedAction action = new PrivilegedAction() {
public Boolean run() { return hasNextService(); }
};
return AccessController.doPrivileged(action, acc);
}
}
public S next() {
if (acc == null) {
return nextService();
} else {
PrivilegedAction action = new PrivilegedAction() {
public S run() { return nextService(); }
};
return AccessController.doPrivileged(action, acc);
}
}
public void remove() {
throw new UnsupportedOperationException();
}
}
nextServiceはClass.forNameでドライバクラスをロードし、ドライバクラスの静的ブロックを実行し、Driver Managerでインスタンスを登録していることがわかります...では、DriverManagerがクラスspiのメカニズムを使用してすべての駆動クラスを自動的にロードする以上、コードを書くときに行く必要はありません.
Class.forName("com.mysql.jdbc.Driver");//1
はい、このコードはもう余計です.削除しても正常にデータベース接続を確立できます.