Mecanismo JAVA SPI

breve introducción

SPI nombre completo del Proveedor de servicios de interfaz, se aplica principalmente a los fabricantes de componentes personalizados o plug-ins. Una descripción más detallada de la documentación java.util.ServiceLoader. Breve resumen de las ideas bajo mecanismos SPI java: nuestro sistema de módulos abstractos, los programas tienden a tener una gran cantidad de diferentes implementaciones, tales como módulo de registro, el módulo de análisis XML, JDBC módulo. diseño orientado a objetos, el codificado basado en la interfaz entre el programa no logra, por lo general recomendamos entre el módulo de clase del módulo. Una vez que el código involucrado en una categoría específica, se viola el principio de acoplamiento activo, si es necesario reemplazar una aplicación, es necesario modificar el código. A fin de lograr que el programa no puede ser especificado en un tiempo dinámico en el módulo de montaje, que requiere un mecanismo de descubrimiento de servicios . Java SPI para proporcionar un mecanismo de este tipo: encontrar un mecanismo para una interfaz de implementación del servicio. COI es algo similar a la idea es mover más allá del control del proceso de montaje, este mecanismo es particularmente importante en un diseño modular.

convenio específico SPI

Java SPI es un acuerdo específico de la siguiente manera: Cuando los servicios del proveedor después de una implementación de la interfaz de servicio, crear un archivo con el nombre de la interfaz de servicio, mientras que el paquete frasco de META-INF / services /. El documento es la realización de la interfaz de la clase de implementación del servicio. Y cuando el módulo de programa externo, de buscar una categoría específica por el nombre del paquete frasco de META-INF / services / en las crea instancias de carga del archivo de configuración y módulo de inyección completa . Usted será capaz de encontrar una buena clase que implementa la interfaz de servicio se basa en un contrato, sin la necesidad de desarrollar código. JDK Ofrecemos un servicio de herramientas de aplicación para encontrar: java.util.ServiceLoader.

Un ejemplo sencillo

En primer lugar, ofrecemos una clase de interfaz IOperationy sus dos clases de implementación PlusOperationImply DivisionOperationImpl, en com.qhong.spiesta ruta del paquete.

package com.qhong.spi;

/**
 * @author qhong
 * @date 2020/3/22 0:16
 **/
public interface IOperation {

    int operation(int numberA, int numberB);
}
package com.qhong.spi;

/**
 * @author qhong
 * @date 2020/3/22 0:17
 **/
public class PlusOperationImpl implements IOperation {

    public PlusOperationImpl() {
        System.out.println("plusOperation construct");
    }

    public int operation(int numberA, int numberB) {
        System.out.println("Operation: plus");

        return numberA + numberB;
    }
}
package com.qhong.spi;

/**
 * @author qhong
 * @date 2020/3/22 0:17
 **/
public class DivisionOperationImpl implements IOperation {

    public DivisionOperationImpl() {
        System.out.println("division construct");
    }

    @Override
    public int operation(int numberA, int numberB) {
        System.out.println("Operation: division");
        return numberA / numberB;
    }
}

Nuevo perfil de spi

Nueva carpeta META-INF bajo los recursos de la carpeta y el archivo de crear /services/com.qhong.spi.IOperation

prueba

public class testSPI {
    public static void main(String[] args) {
        ServiceLoader<IOperation> operations = ServiceLoader.load(IOperation.class);

        int numberA = 6;
        int numberB = 3;
        System.out.println("NumberA: " + numberA + ", NumberB: " + numberB);
        Iterator<IOperation> iterator = operations.iterator();
        while (iterator.hasNext()) {
            IOperation operation = iterator.next();
            System.out.println(operation.operation(numberA, numberB));
        }
    }
}

Salida:

NumberA: 6, NumberB: 3
division construct
Operation: division
2
plusOperation construct
Operation: plus
9

principio de aplicación SPI

La aplicación llama al método ServiceLoader.load

Dentro método ServiceLoader.load para crear un nuevo ServiceLoader, y crear instancias de las variables miembro de clase, incluyendo:

  • ClassLoader cargador (cargador de clases)
  • AccessControlContext acc (controlador de acceso)
  • LinkedHashMap <String, S> proveedores (tampón de carga para la clase de éxito)
  • LazyIterator lookupIterator (iterador implementar la función)

La aplicación obtiene una instancia de objeto a través del iterador

ServiceLoader variables miembro para determinar si hay una instancia de objeto proveedores de memoria caché del objeto, si hay una memoria caché, el retorno directo.

Si no caché, una carga de clase de ejecución: leer el archivo de configuración META-INF / services / bajo, una instancia para obtener los nombres de todas las clases, por el método de la reflexión la Class.forName () objeto de clase cargado, y se lavó con instancia () el método de la clase se crea una instancia. La caché de clase en los proveedores de instancia instancia de objeto de los objetos y luego regresa.

Por ejemplo JDBC

java.sql.DriverManager

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

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

java.util.ServiceLoader

    public static <S> ServiceLoader<S> load(Class<S> service) {
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        return ServiceLoader.load(service, cl);
    }

cita

  public static <S> ServiceLoader<S> load(Class<S> service,
                                            ClassLoader loader)
    {
        return new ServiceLoader<>(service, loader);
    }

cita

  private ServiceLoader(Class<S> svc, ClassLoader cl) {
        service = Objects.requireNonNull(svc, "Service interface cannot be null");
        loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
        acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
        reload();
    }

cita

   public void reload() {
        providers.clear();
        lookupIterator = new LazyIterator(service, loader);
    }

cita

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

    }

Tenga en cuenta que driversIterator.next()la final es llamar Class.forName(DriverName, false, loader)al método

Debido a que la frase Class.forName(DriverName, false, loader)reside el código de clase en la java.util.ServiceLoaderclase, y ServiceLoader.classcargaron BootrapLoader, y por lo tanto no pueden necesariamente pasan forName el cargador es BootrapLoader, esta vez sólo se puede utilizar CTCE, y que no es su propio cargador de clases para cargar el CTCE (adquirida por Thread.currentThread (), es simplemente hacer trampa ah!). El artículo anterior también habló sobre el final de la CTCE por defecto se encuentra ejecutando el código en la aplicación de la AppClassLoader cargador de clases del sistema.

Mirar hacia abajo para ver el ServiceLoader.load(Class)código

    public static <S> ServiceLoader<S> load(Class<S> service) {
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        return ServiceLoader.load(service, cl);
    }

ContextClassLoader referencias de almacenamiento predeterminado AppClassLoader, ya que se ejecuta cuando se coloca en el hilo, por lo que no importa en qué lugar del programa actual (BootstrapClassLoader o ExtClassLoader, etc.), en cualquier momento de necesidad se pueden utilizar Thread.currentThread (). getContextClassLoader () quitar las necesidades cargador de clases de aplicaciones para completar la operación.

Casi aquí para explicar el mecanismo de SPI. Sin rodeos decir es que (JDK) ofrece una ayuda que (ejecutores de terceros) los servicios de carga (tales como, la base de datos de registro en bases de datos) forma conveniente, siempre y cuando usted sigue la convención (el nombre de clase escrito en el directorio / META-INF años), que cuando comienzo me gustaría analizar todos los nombres de los paquetes del tarro cumplen con el contrato, a continuación, llamar forName cargado, pero mi cargador de clases no está cargado, luego cargarla en el hilo que se está ejecutando en el CTCE, Seguimiento hasta la forma en que desea operar (unidad para darse cuenta de la clase de bloque de código estático) es lo suyo.

resumen

JDK mecanismo incorporado de SPI en sí tiene sus ventajas, pero es relativamente simple, hay muchas deficiencias.

ventaja

Uso Java ventaja SPI desacoplamiento mecanismo es tal que la definición de interfaz de servicio específica para lograr la separación, no acoplados entre sí. O, alternativamente, se puede habilitar componentes específicos de aplicación de acuerdo con la situación real del tráfico.

defecto

  • No se puede cargar bajo demanda. Aunque ServiceLoader hizo a la carga demora, pero la lata básica sólo se consigue mediante el desplazamiento de todo, es decir, la clase de implementación de interfaz tiene que ser cargada y una instancia de nuevo. Si no se quiere lograr cierta clase o tiempo de instancias cierta clase, sino que también puede ser cargada y una instancia, lo que dio lugar a residuos.
  • Se no es flexible, sólo puede obtenerse una clase de implementación a través del formulario de iterador, no para conseguir las clases de implementación correspondientes sobre la base de algunos parámetros.
  • Ejemplos del uso de múltiples clase multi-threading ServiceLoader concurrente es inseguro.
  • La carga no lanzar una excepción, no la verdadera razón de la clase que implementa, el error difícil de localizar.

Dadas las muchas deficiencias de SPI, muchos sistemas están configurados para realizar su mecanismo de carga de clases, tales como Dubbo . Los usuarios pueden personalizar también cargador de clases + carga de reflexión para lograr no complicado. También las soluciones de código abierto tienen una clase cargador de estructura de plugins para Java (PF4J) y así sucesivamente.

referencia:

análisis mecanismo de Java SPI y sus ventajas y desventajas

Realmente entender cargador de clases contexto rosca (análisis de casos múltiple)

Supongo que te gusta

Origin www.cnblogs.com/hongdada/p/12543654.html
Recomendado
Clasificación