From dubbo to jdbc and spi

Recently, I was looking at the source code of Dubbo. The function of dubbo is based on the extension point (Extension). If you want to modify which module, you can easily extend and replace it.

This extension point is based on the idea of ​​spi, but dubbo does not use jdk's native serviceLoader, but implements ExtensionLoader to load extension points, supports key-value pairs, is more flexible, and follows basically the same specifications. This is off topic.

 

What is SPI? What can SPI do? Here is an introductory article -- link

 

When I first came into contact with SPI, I was a little confused. I found that many articles took jdbc as a typical example of SPI.

Recalling that when I first went to college, frameworks such as hibernate and mybatis were not yet popular, so I could only write the code for the jdbc link database by myself, like this

        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 ();
        }

 

 

The function of this Class.forName("com.mysql.jdbc.Driver") is to load the database driver class com.mysql.jdbc.Driver, the code is as follows

 

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!");
        }
    }
}

 

 

When loading, the static code block in the above code will be executed first, through

DriverManager.registerDriver(new Driver());

com.mysql.jdbc.driver creates a new instance of its own and registers it with DriverManager, so that DriverManager can use the driver to obtain database connections.

So far, the driver has been loaded, and there is no application about SPI. I am also confused, and I don't understand what is the relationship between SPI and jdbc. But when I think about it, if jdbc uses spi, then there must be a corresponding implementation in DriverManager, continue to look at the DriverManager code, there are static blocks as follows

    static {
        loadInitialDrivers();
        println("JDBC DriverManager initialized");
    }

 The code of the loadInitialDrivers() method is as follows

    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);
            }
        }
    }

 Note that ServiceLoader is called in this method to load the driver file

 

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

 

Finally found the figure of SPI. If you continue to watch, you will find that the loading of the driver class is carried out during the traversal.

                Iterator<Driver> driversIterator = loadedDrivers.iterator();
                try{
                    while(driversIterator.hasNext()) {
                        driversIterator.next();
                    }
                } catch(Throwable t) {
                // Do nothing
                }

 loadedDrivers.iterator() returns

private LazyIterator lookupIterator;

 LazyIterator is an internal private class of ServiceLoader, which implements the terator interface. The code is as follows

    // 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();
        }

    }

 It can be seen that nextService will use Class.forName to load the driver class, then execute the static block in the driver class, and then register an instance of DriverManager. . . . Then since DriverManager uses the spi-like mechanism to automatically load all driver classes, we don't need to go to the code when we write the code.

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

 Yes, this line of code is redundant. After removing it, the database connection can still be established normally.

 

 

 

 

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326298903&siteId=291194637