`
千年松鼠
  • 浏览: 25962 次
文章分类
社区版块
存档分类
最新评论

从dubbo到 jdbc 与 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了一个自己的实例,并注册给了DriverManager,这样DriverManager就能够使用驱动程序去获得数据库连接。

到此为止,驱动已经加载完毕,没有任何关于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<String>() {
                public String run() {
                    return System.getProperty("jdbc.drivers");
                }
            });
        } catch (Exception ex) {
            drivers = null;
        }

        AccessController.doPrivileged(new PrivilegedAction<Void>() {
            public Void run() {
                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;
            }
        });

        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<Driver> loadedDrivers = ServiceLoader.load(Driver.class);

 

终于找到了SPI的身影,继续看会发现驱动类的加载是在遍历的时候进行的

                Iterator<Driver> 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<S>
    {

        Class<S> service;
        ClassLoader loader;
        Enumeration<URL> configs = null;
        Iterator<String> pending = null;
        String nextName = null;

        private LazyIterator(Class<S> 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<Boolean> action = new PrivilegedAction<Boolean>() {
                    public Boolean run() { return hasNextService(); }
                };
                return AccessController.doPrivileged(action, acc);
            }
        }

        public S next() {
            if (acc == null) {
                return nextService();
            } else {
                PrivilegedAction<S> action = new PrivilegedAction<S>() {
                    public S run() { return nextService(); }
                };
                return AccessController.doPrivileged(action, acc);
            }
        }

        public void remove() {
            throw new UnsupportedOperationException();
        }

    }

 可以看到nextService是会用Class.forName去加载驱动类,然后执行驱动类中静态块,然后DriverManager注册一个实例。。。。那么既然DriverManager使用类spi的机制去自动加载所有驱动类,我们在写代码的时候就无需再去

Class.forName("com.mysql.jdbc.Driver");//1

 是的,这行代码已经多余了,去掉之后仍然可以正常建立数据库连接,爽

 

 

 

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics