dubboからjdbcとspiへ

9302 ワード

詳細
最近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

 はい、このコードはもう余計です.削除しても正常にデータベース接続を確立できます.