ClassLoader personalizado para omitir el experimento de delegación parental

 1. Para cualquier clase, la unicidad de la máquina virtual Java está determinada por el ClassLoader que la carga y por sí misma.
En otras palabras, comparando dos clases, solo si todas están cargadas por el mismo ClassLoader, entonces la comparación es significativa. De lo contrario, incluso si es el mismo archivo de clase, si el cargador de clases que los carga es diferente, las dos clases no deben ser iguales.

2. El mecanismo de delegación padre de carga de clases se puede destruir cambiando CallClassLoader y ContextClassLoader.
La carga de la clase a la que hace referencia la clase actual también la carga el ClassLoader que carga la clase actual, y el ContextClassLoader del subproceso secundario se deriva del ContextClassLoader del subproceso principal.

3. La extensión de ClassLoader generalmente debería recomendar reescribir el método findClass (String).
La razón es que el cargador de clases personalizado solo se enfoca en cargar su propia clase. En el proceso de carga de su propia clase, la clase principal Object se cargará primero. La clase de objeto es cargada por el cargador de clases de inicio y puede ser cargada directamente por loadClass (Cadena) por defecto. Ejecución, para hacerlo, puede usar ClassLoader para cargar selectivamente ciertas clases para el aislamiento.

package com.mashibing.jvm.c2_classloader;

import java.io.InputStream;
import java.lang.reflect.Method;

public class MySelfClassLoader extends ClassLoader {
    public void say() {
        System.out.println("hello world");
    }

    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        try {
            String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
            InputStream is = getClass().getResourceAsStream(fileName);
            if (is == null) {
                return super.loadClass(name);
            }
            byte[] b = new byte[is.available()];
            is.read(b);
            return defineClass(name, b, 0, b.length);
        }
        catch (Exception e) {
            throw new ClassNotFoundException(name);
        }
    }
    public static void main(String[] args) throws Exception {
        // 打印java虚拟机的ClassLoader
        System.out.println(Thread.currentThread().getContextClassLoader());
        System.out.println(Thread.currentThread().getContextClassLoader().getParent());
        System.out.println(Thread.currentThread().getContextClassLoader().getParent().getParent());
        // 自定义ClassLoader
        ClassLoader myLoader = new MySelfClassLoader();
        Class<?> clazz = myLoader.loadClass("com.mashibing.jvm.c2_classloader.MySelfClassLoader");
       // Class<?> clazz = myLoader.loadClass("com.mashibing.jvm.Hello");
//加载别的类显示的 ClassLoader就不是MySelfClassLoader,是我电脑环境的问题,公司电脑就正常,真tm绝了,
//都是jdk1.8,win10,ide2018 ,就除了是电脑牌子不一样,别的不知道是啥了,有小伙伴遇到过这样的问题吗

        Object obj = clazz.newInstance();
        // 打印自定义ClassLoader加载的Class对象
        System.out.println(obj.getClass());
        // 打印被加载的Class对象是由哪个ClassLoader加载的
        System.out.println(obj.getClass().getClassLoader());
        /*
         * 对于任意一个类,由加载它的ClassLoader和它本身决定了在jvm虚拟机中的唯一性。
         * 也就是说比较2个类,只有它们都是由同一个ClassLoader加载,那么比较才有意义。
         * 否则,即使是同一个类文件,只要加载它们的ClassLoader不同,那么这2个类必定不相等。
         */
        System.out.println(obj instanceof com.mashibing.jvm.c2_classloader.MySelfClassLoader);
        //System.out.println(obj instanceof com.mashibing.jvm.Hello);

        // 由自定义ClassLoader加载后,在程序里运行。
        Method method = clazz.getDeclaredMethod("say", new Class<?>[] {});
        method.invoke(obj, new Object[] {});
        // 获取当前上下文的ClassLoader
        System.out.println(Thread.currentThread().getContextClassLoader());
        // 改变上下文的ClassLoader
        Thread.currentThread().setContextClassLoader(myLoader);
        // 获取当前上下文的ClassLoader
        System.out.println(Thread.currentThread().getContextClassLoader());
        // 改变当前上下文的ClassLoader可以改变在当前线程派生出的子线程的上下文ClassLoader
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                Thread t2 = Thread.currentThread();
                System.out.println(t2.getName() + ":" + t2.getContextClassLoader());
            }
        });
        t.start();
        Thread.sleep(3000);
        // 获取CallClassLoader
        Class<?> clz = Class.forName("com.mashibing.jvm.c2_classloader.MySelfClassLoader");
        System.out.println(clz);
        System.out.println(clz.getClassLoader());
        System.out.println(clz.getClass());
        System.out.println(clz.getClass().getClassLoader());
    }
}

Resultado de la ejecución:
sun.misc.Launcher$AppClassLoader@addbf1 ==> El cargador de clases de contexto actual es el cargador de clases de aplicaciones
sun.misc.Launcher$ExtClassLoader@42e816 ==> El cargador de clases padre del cargador de clases de aplicaciones es la clase de extensión loader 器
null ==> El cargador de clases padre del cargador de clases extendido es el cargador de clases de inicio. Debido a que el cargador de clases de inicio está escrito en C ++, la
clase no se puede obtener en java. com.zoo.classloader.ClassLoaderTest ==> Cargador de clases personalizado Cargar para cargar
com.zoo.classloader.MyClassLoader@1fb8ee3 ==> obtener su cargador de clases
falso a través del objeto Clase ==> la misma Clase, diferentes cargas del cargador de clases, entonces la Clase no es igual
hola mundo ==> es auto Definir la clase cargada por el cargador de clases para ejecutar
sun.misc.Launcher$AppClassLoader@addbf1 ==> Obtener el cargador de clases de contexto actual
com.zoo.classloader.MyClassLoader@1fb8ee3 ==> Establecer el cargador de clases de contexto
Thread-0 : com.zoo.classloader.MyClassLoader@1fb8ee3 ==> la clase de cargador de
clase de subproceso derivado com.zoo.classloader.ClassLoaderTest ==> clase de carga predeterminada
sun.misc.Launcher$AppClassLoader@addbf1 ==> clase de aplicación de carga predeterminada cargador
class java.lang.Class ==> Get Class
null ==> Obtiene el cargador de clases. Dado que es el cargador de clases de inicio, es nulo. Las clases básicas de Java son cargadas por el cargador de clases de inicio.

 

¿Por qué quiere romper la delegación de los padres?

Romper la delegación principal y reiniciar ClassLoader.
1. Las bibliotecas de clases de Java utilizadas por dos aplicaciones web implementadas en el mismo servidor se pueden aislar entre sí. Este es el requisito más básico. Dos aplicaciones diferentes pueden depender de diferentes versiones de la misma biblioteca de clases de terceros. No puede exigir que una biblioteca de clases tenga solo una copia en un servidor. El servidor debe asegurarse de que las bibliotecas de clases de las dos se pueden utilizar aplicaciones.

2. Las bibliotecas de clases de Java utilizadas por dos aplicaciones web implementadas en el mismo servidor se pueden compartir entre sí. Este requisito también es muy común, por ejemplo, 10 aplicaciones de la misma biblioteca de clases Spring no se pueden almacenar por separado en el directorio de aislamiento de cada aplicación.

3. Soporte de reemplazo en caliente. Sabemos que los archivos JSP deben compilarse en archivos .class para ser ejecutados por la máquina virtual. Sin embargo, debido a sus características de almacenamiento de texto puro, la probabilidad de modificación en tiempo de ejecución de los archivos JSP es mucho mayor que la de bibliotecas de clases de terceros o sus propios archivos .class, y las aplicaciones web como JSP también consideran la necesidad de reiniciar después de la modificación como una gran ventaja

Debido a los problemas mencionados anteriormente, el ClassLoader proporcionado por Java a los usuarios no puede satisfacer la demanda. El servidor Tomcat tiene su propia arquitectura ClassLoader, por supuesto, todavía se basa en el modelo de delegación principal

Reimpreso de: http://lifestack.cn/archives/256.html

Supongo que te gusta

Origin blog.csdn.net/zhaofuqiangmycomm/article/details/113827335
Recomendado
Clasificación