Subsistema cargador de clases JVM y SPI

Cargador de clases

Hay dos tipos de cargadores en JVM, uno está escrito por C ++, el otro está escrito por JAVA, excepto el Bootstrap Class Loader (Bootstrap Class Loader) está escrito por C ++, los otros están escritos por JAVA, Los cargadores escritos en JAVA heredan de java.lang.ClassLoader. JVM también soporta el cargador personalizado. El uso típico del cargador personalizado es destruir la delegación parental de JVM. ¿Por qué deberíamos destruir la delegación parental y queremos destruir a ambos padres? El proyecto delegado debe tener este requisito. Por ejemplo, nuestro servidor web tomcat tiene un cargador de clases personalizado. No solo el servidor web, sino también los marcos de tecnología convencionales actuales personalizarán el cargador de clases y se puede cargar el cargador de clases personalizado. La clase que desea cargar no solo puede cargar localmente, sino también cargar archivos de clase desde Internet.
Los cargadores de clases de JVM incluyen: cargador de clases de inicio, cargador de clases extendido, cargador de clases de aplicaciones, cargador de clases personalizado; el cargador de clases de JVM pertenece al modo de delegación padre, es decir, cuando mi cargador de clases necesita cargar uno Cuando sea clase, no completará esta operación por sí mismo. Delegará la solicitud hacia arriba hasta que el cargador de clases comience a cargar. Si no se carga al final, es decir, cuando no se encuentra la clase completamente calificada, la JVM arrojará Una excepción ClassNotFound, la siguiente figura muestra la estructura de nuestro cargador de clases: la
Inserte la descripción de la imagen aquí
figura anterior es el diagrama de estructura del cargador de clases, de la figura podemos ver que queremos cargar nuestra propia clase está delegada a nuestro cargador de clases padre Hecho; el cargador de clases de inicio de nivel superior está escrito en C ++, y no podemos verificar su implementación. Los demás son todos cargadores de clases implementados en Java. Por lo tanto, el programa Java no puede llamar al cargador de clases de inicio
. Como otro cargador de clases tiene entidad, no tiene entidad. JVM define un conjunto de lógica que C ++ maneja la carga de clases como el cargador de clases de inicio.

Iniciar el cargador de clases

Ver la ruta de carga del cargador de clases de inicio

URL[] urLs = Launcher.getBootstrapClassPath().getURLs();
for (URL urL : urLs) {
    
    
    System.out.println(urL);
}

Salida:
archivo: / D: /tools/java8_212/jre/lib/resources.jar
archivo: / D: /tools/java8_212/jre/lib/rt.jar
archivo: / D: / tools / java8_212 / jre / lib /
archivo sunrsasign.jar : / D: /tools/java8_212/jre/lib/jsse.jar
archivo: / D: /tools/java8_212/jre/lib/jce.jar
archivo: / D: / tools / java8_212 / jre / lib
Archivo /charsets.jar : / D: /tools/java8_212/jre/lib/jfr.jar
archivo: / D: / tools / java8_212 / jre / classes
Puede ver que el cargador de clases de inicio carga principalmente el núcleo bajo nuestro jre paquete

Cargador de clases extendido

  ClassLoader classLoader = ClassLoader.getSystemClassLoader().getParent();

        URLClassLoader urlClassLoader = (URLClassLoader) classLoader;

        URL[] urls = urlClassLoader.getURLs();
        for (URL url : urls) {
    
    
            System.out.println(url);
        }

archivo: / D: /tools/java8_212/jre/lib/ext/access-bridge-64.jar
archivo: / D: /tools/java8_212/jre/lib/ext/cldrdata.jar
archivo: / D: / tools / Archivo java8_212 / jre / lib / ext / dnsns.jar
: / D: /tools/java8_212/jre/lib/ext/jaccess.jar
archivo: / D: /tools/java8_212/jre/lib/ext/jfxrt.jar
archivo : / D: /tools/java8_212/jre/lib/ext/localedata.jar
archivo: / D: /tools/java8_212/jre/lib/ext/nashorn.jar
archivo: / D: / tools / java8_212 / jre / lib
Archivo /ext/sunec.jar : / D: /tools/java8_212/jre/lib/ext/sunjce_provider.jar
archivo: / D: /tools/java8_212/jre/lib/ext/sunmscapi.jar
archivo: / D: / tools / java8_212 / jre / lib / ext / sunpkcs11.jar
archivo: / D: /tools/java8_212/jre/lib/ext/zipfs.jar 加载
ext 扩展 目录 的 jar

Cargador de clases de aplicaciones

Cargador de clases que carga programas de usuario por defecto

Ver la ruta cargada por el cargador de clases

 String[] urls = System.getProperty("java.class.path").split(":");

        for (String url : urls) {
    
    
            System.out.println(url);
        }

        System.out.println("================================");

        URLClassLoader classLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();

        URL[] urls1 = classLoader.getURLs();
        for (URL url : urls1) {
    
    
            System.out.println(url);
        }

Cómo almacenar la clase cargada por el cargador de clases

Inserte la descripción de la imagen aquí
Se puede ver en la figura anterior que diferentes cargadores de clases abrirán una parte de su propio espacio en el área de métodos después de cargar la clase en el área de métodos. Es decir, después de que diferentes cargadores de clases hayan cargado la misma clase en el área de métodos, su memoria La dirección es diferente, porque la dirección en el área de método es diferente y el proceso de carga de JVM es delegación de los padres, ¿por qué necesita la delegación de los padres? ? Parte de la razón es evitar que nuestra misma clase sea cargada por diferentes cargadores, por lo que tendrá múltiples copias en la memoria, por lo que usar el mecanismo de delegación padre puede evitar esta situación.
Cuando queremos obtener un objeto de una clase, necesitamos obtener la clase del área de método, que es el proceso de carga de la clase (a través de una serie de procesos) y luego inicializar el espacio de creación en el área del montón para almacenar los objetos que creamos.

Delegación de los padres

¿Qué es la delegación principal? La delegación principal es un mecanismo de JVM cuando se cargan clases. Siempre que haya una solicitud de carga de clase, delegará la solicitud a su clase principal y permitirá que la clase principal complete la carga de la clase por sí misma. Delegación online, hasta que se delegue a la carga de la clase de inicio, si no se carga al final, se lanzará una capa ClassNotFound; la siguiente figura muestra la descripción de la delegación padre:
Inserte la descripción de la imagen aquí

Romper la delegación de los padres

Porque en algunos casos, el cargador de clases padre necesita delegar en el cargador de clases hijo para cargar el archivo de clase. Debido a la limitación del rango de carga, el cargador de clases principal no puede cargar el archivo requerido. Tome la interfaz del controlador como ejemplo. Debido a que la interfaz del controlador está definida en jdk, su implementación la proporciona el proveedor de servicios de cada base de datos, como mysql. MySQL Connector, entonces surge el problema. DriverManager (también proporcionado por jdk) necesita cargar cada clase de implementación que implementa la interfaz del Driver y luego administrarla, pero DriverManager es cargado por el cargador de clases de inicio y solo puede registrar los archivos en la biblioteca de JAVA_HOME. La implementación es proporcionada por el proveedor de servicios y cargada por el cargador de clases del sistema. En este momento, debe iniciar el cargador de clases para delegar la subclase para cargar la implementación del controlador, lo que rompe la delegación principal.

En situaciones como esta, es necesario romper la delegación de los padres.

Romper la delegación de los padres significa en realidad no delegar, delegar

Mecanismo SPI

Otro mecanismo que rompe la delegación de los padres es el mecanismo SPI.
Qué es SPI SPI, el nombre completo es Service Provider Interface, es un mecanismo de descubrimiento de servicios

Busca archivos en la carpeta META-INF / services debajo de la ruta ClassPath y carga automáticamente las clases definidas en los archivos.
Este mecanismo ofrece la posibilidad de muchas extensiones de marco, como el mecanismo SPI utilizado en Dubbo y JDBC. Veamos primero cómo se usa a través de un ejemplo muy simple:

Primero creo 4 proyectos:
xxx-core Crear una interfaz PayService
xxx-dev1 Crear una clase de prueba
xxx-dev2 Crear una clase de pago WeChat para implementar PayService
xxx-dev3 Crear una clase de pago Alipay para implementar PayService

Como sigue:
xxx-core:
PayService:

public interface PayService {
    
    
    void pay();
}

xxx-dev2:
WxPayService:

public class WxPayService implements PayService {
    
    
    @Override
    public void pay() {
    
    
        System.out.println("Wxpay....");
    }
}

xxx-dev3:
AliPayService

public class AliPayService implements PayService {
    
    
    @Override
    public void pay() {
    
    
        System.out.println("AliPay....");
    }
}

También necesitamos crear un archivo de servicio bajo los recursos de los proyectos dev2 y dev3:
Inserte la descripción de la imagen aquí

Donde com.xxx. **. PayService es su clase de implementación

Escribamos nuestra clase de prueba:
xxx-dev1:

public class SpiTest {
    
    

    public static void main(String[] args) {
    
    
        ServiceLoader<PayService> services = ServiceLoader.load(PayService.class);
        services.forEach(item ->{
    
    
            item.pay();
        });
    }
}

El descubrimiento del servicio se realiza a través del mecanismo SPI ServiceLoader proporcionado por jdk;
esta operación no puede generar nada, pero cuando introducimos xxx-dev2 en el proyecto dev1, generará Wxpay ...,
cuando cambiamos el proyecto dev1 a dev2, lo hará Salida AliPay ...
Si introducimos tanto dev1 como dev2, ambos se encontrarán y registrarán, luego se generará:
Wxpay ...
AliPay ...

Supongo que te gusta

Origin blog.csdn.net/scjava/article/details/108249563
Recomendado
Clasificación