1. Explicación detallada del mecanismo SPI de Java + conocimiento general de Springboot [Springboot]

1. ¿Qué es un comentario?

Las anotaciones en Java son en realidad muy simples. Se pueden entender como elementos de etiquetado (clases, atributos, métodos, etc.). Al mismo tiempo, las anotaciones pueden llevar algunos atributos. Estos atributos pueden tener valores predeterminados
en tiempo de ejecución. Podemos obtener la anotación del elemento a través de la reflexión, y realizar las operaciones correspondientes de acuerdo con la anotación del elemento y el atributo de la anotación.

2. ¿Qué son las meta anotaciones?

  • El papel de las meta-anotaciones es anotar otras anotaciones. Java define cuatro tipos de meta-anotaciones estándar, que se utilizan para proporcionar explicaciones para otros tipos de anotaciones.
  • Estos tipos y las clases que admiten se pueden encontrar en el paquete java.lang.annotation (@Target, @Retention, @Documented, @Inherited).
    • @Target: se usa para describir el alcance de uso de las anotaciones (es decir, dónde se pueden usar las anotaciones descritas, como clases, métodos o constructores)
    • @Retención: indica en qué nivel se debe guardar la información de la anotación, que se utiliza para describir el ciclo de vida de la anotación (FUENTE <CLASE <TIEMPO DE  EJECUCIÓN )
    • @Document: Explique que la anotación se incluirá en el javadoc
    • @Inherited: Explique que la subclase puede heredar la anotación en la clase principal.

 

 

1. ¿Qué es SPI?

   El nombre completo de SPI es (Service Provider Interface), que es un mecanismo de descubrimiento de servicios integrado en el JDK . SPI es un mecanismo de descubrimiento de reemplazo dinámico. Por ejemplo, si hay una interfaz, si desea agregarle dinámicamente una implementación en tiempo de ejecución, solo necesita agregar una implementación. Lo que encontramos a menudo es la interfaz java.sql.Driver. Diferentes fabricantes pueden realizar diferentes implementaciones para la misma interfaz. Tanto mysql como postgresql tienen diferentes implementaciones para los usuarios. El mecanismo SPI de Java puede encontrar servicios para una determinada interfaz.

 

Como se muestra en la figura anterior, la interfaz corresponde a la interfaz SPI abstracta; el implementador implementa la interfaz SPI; la persona que llama depende de la interfaz SPI.

La definición de la interfaz SPI está en la persona que llama, que depende más conceptualmente de la persona que llama; la organización está ubicada en el paquete donde se encuentra la persona que llama y la implementación se encuentra en un paquete separado.

Cuando el proveedor de servicios proporciona una implementación de una interfaz, debe crear un archivo con el nombre de la interfaz de servicio en el directorio META-INF / services / bajo la ruta de clase. El contenido de este archivo es la clase de implementación específica de esta interfaz. Cuando otros programas necesitan este servicio, puede buscar el archivo de configuración en META-INF / services / del paquete jar (generalmente basado en el paquete jar). El archivo de configuración contiene el nombre de clase de implementación específico de la interfaz. cárguelo y cree una instancia en función de este nombre de clase, y podrá utilizar el servicio. La clase de herramienta para encontrar la implementación del servicio en JDK es: java.util.ServiceLoader.

2. El propósito de SPI

Database DriverManager, Spring, ConfigurableBeanFactory, etc. todos usan el mecanismo SPI Aquí tomamos la base de datos DriverManager como ejemplo para ver la historia interna de su implementación.

DriverManager es una herramienta para administrar y registrar diferentes controladores de bases de datos en jdbc. Para una base de datos, puede haber diferentes implementaciones de controladores de base de datos. Cuando usamos una implementación de controlador específica, no queremos modificar el código existente, pero esperamos que una configuración simple pueda lograr el efecto. Al usar el controlador mysql, surge una pregunta: ¿cómo obtiene DriverManager una determinada clase de controlador? Después de usar Class.forName ("com.mysql.jdbc.Driver") para cargar el controlador mysql, ejecutaremos el código estático para registrar el controlador en DriverManager para su uso posterior.

Implementación del controlador

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

En el bloque de código estático de la clase de controlador, llame al método de registro de controlador nuevo de DriverManager y páselo al administrador de controladores como parámetro.

Implementación de Mysql DriverManager

    /**
     * Load the initial JDBC drivers by checking the System property
     * jdbc.properties and then use the {@code ServiceLoader} mechanism
     */
    static {
        loadInitialDrivers();
        println("JDBC DriverManager initialized");
    }

Puede ver que hay un loadInitialDriversmétodo en su bloque de código estático interno, y el loadInitialDriversuso usa la clase de herramienta spi mencionada anteriormente ServiceLoader:

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;
        }
        // If the driver is packaged as a Service Provider, load it.
        // Get all the drivers through the classloader
        // exposed as a java.sql.Driver.class service.
        // ServiceLoader.load() replaces the sun.misc.Providers()

        AccessController.doPrivileged(new PrivilegedAction<Void>() {
            public Void run() {

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

                /* Load these drivers, so that they can be instantiated.
                 * It may be the case that the driver class may not be there
                 * i.e. there may be a packaged driver with the service class
                 * as implementation of java.sql.Driver but the actual class
                 * may be missing. In that case a java.util.ServiceConfigurationError
                 * will be thrown at runtime by the VM trying to locate
                 * and load the service.
                 *
                 * Adding a try catch block to catch those runtime errors
                 * if driver not available in classpath but it's
                 * packaged as service and that service is there in classpath.
                 */
                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);
            }
        }

Primero busque el valor del atributo jdbc.drivers y luego busque el controlador a través del mecanismo SPI

public final class ServiceLoader<S>
    implements Iterable<S>
{

    private static final String PREFIX = "META-INF/services/";
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;
        }

Puede ver que el recurso con el nombre de clase nombre de archivo (aquí equivalente a Driver.class.getName ()) en la carpeta META-INF / services / se carga y luego se carga en la máquina virtual.

Tenga en cuenta que hay una oración "Cargue estos controladores, para que se puedan crear instancias". Significa cargar los controladores escaneados por SPI para activar su inicialización. Eso activa su bloque de código estático

/**
     * Registers the given driver with the {@code DriverManager}.
     * A newly-loaded driver class should call
     * the method {@code registerDriver} to make itself
     * known to the {@code DriverManager}. If the driver is currently
     * registered, no action is taken.
     *
     * @param driver the new JDBC Driver that is to be registered with the
     *               {@code DriverManager}
     * @param da     the {@code DriverAction} implementation to be used when
     *               {@code DriverManager#deregisterDriver} is called
     * @exception SQLException if a database access error occurs
     * @exception NullPointerException if {@code driver} is null
     * @since 1.8
     */
    public static synchronized void registerDriver(java.sql.Driver driver,
            DriverAction da)
        throws SQLException {

        /* Register the driver if it has not already been added to our list */
        if(driver != null) {
            registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
        } else {
            // This is for compatibility with the original DriverManager
            throw new NullPointerException();
        }

        println("registerDriver: " + driver);

    }

Regístrese en la lista de unidades del administrador de unidades

public class DriverManager {


    // List of registered JDBC drivers
    private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();

Cuando obtenga la conexión, llame al método de conexión del administrador de controladores para obtenerlo de la lista.

  @CallerSensitive
    public static Connection getConnection(String url,
        String user, String password) throws SQLException {
        java.util.Properties info = new java.util.Properties();

        if (user != null) {
            info.put("user", user);
        }
        if (password != null) {
            info.put("password", password);
        }

        return (getConnection(url, info, Reflection.getCallerClass()));
    }
 private static Connection getConnection(
        String url, java.util.Properties info, Class<?> caller) throws SQLException {
        /*
         * When callerCl is null, we should check the application's
         * (which is invoking this class indirectly)
         * classloader, so that the JDBC driver class outside rt.jar
         * can be loaded from here.
         */
        ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
        synchronized(DriverManager.class) {
            // synchronize loading of the correct classloader.
            if (callerCL == null) {
                callerCL = Thread.currentThread().getContextClassLoader();
            }
        }

        if(url == null) {
            throw new SQLException("The url cannot be null", "08001");
        }

        println("DriverManager.getConnection(\"" + url + "\")");

        // Walk through the loaded registeredDrivers attempting to make a connection.
        // Remember the first exception that gets raised so we can reraise it.
        SQLException reason = null;

        for(DriverInfo aDriver : registeredDrivers) {
            // If the caller does not have permission to load the driver then
            // skip it.
            if(isDriverAllowed(aDriver.driver, callerCL)) {
                try {
                    println("    trying " + aDriver.driver.getClass().getName());
                    Connection con = aDriver.driver.connect(url, info);
                    if (con != null) {
                        // Success!
                        println("getConnection returning " + aDriver.driver.getClass().getName());
                        return (con);
                    }
                } catch (SQLException ex) {
                    if (reason == null) {
                        reason = ex;
                    }
                }

            } else {
                println("    skipping: " + aDriver.getClass().getName());
            }

        }

        // if we got here nobody could connect.
        if (reason != null)    {
            println("getConnection failed: " + reason);
            throw reason;
        }

        println("getConnection: no suitable driver found for "+ url);
        throw new SQLException("No suitable driver found for "+ url, "08001");
    }
private static boolean isDriverAllowed(Driver driver, ClassLoader classLoader) {
        boolean result = false;
        if(driver != null) {
            Class<?> aClass = null;
            try {
                aClass =  Class.forName(driver.getClass().getName(), true, classLoader);
            } catch (Exception ex) {
                result = false;
            }

             result = ( aClass == driver.getClass() ) ? true : false;
        }

        return result;
    }

Supongo que te gusta

Origin blog.csdn.net/zw764987243/article/details/111503789
Recomendado
Clasificación