java classLoad 类加载机制及类隔离

java classLoad 类隔离

前置知识

1. 不同类:JVM 中一个类的唯一标识是 类加载器+类名, 这里类加载器指的是类加载器的实例,并不是一定要定义两个不同类加载器

2. 类加载传导规则:JVM 会选择当前类的类加载器来加载所有该类的引用类

默认类加载机制

demo


package cn.jiayeli.riverMpas.tests;

import org.junit.Test;

/**
 * @author: jiayeli.cn
 * @description multi version jas supported class load
 * @date: 2023/8/6 下午2:03
 */
public class ClassLoadTestCase {
    public  void classLoadTestCase() {
        TestA testA = new TestA();
        TestB testb = new TestB();
        testA.hello();
        testb.hello();
    }

}


class  TestA {
    public void hello() {
        System.out.println("TestA: " + this.getClass().getClassLoader());
 }
}

class TestB {

    public void hello() {
        System.out.println("TestB: " + this.getClass().getClassLoader());
    }
}

output

TestA: jdk.internal.loader.ClassLoaders$AppClassLoader@6bc7c054
TestB: jdk.internal.loader.ClassLoaders$AppClassLoader@6bc7c054

结论

使用双亲委派机制进行加载,两个类用为同一classLoad加载

自定义类加载器实现类隔离

重写ClassLoader.findClass来自定义内加载器

testCase

package cn.jiayeli.riverMpas.tests;

import org.junit.Test;

import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;

/**
 * @author: jiayeli.cn
 * @description multi version jars supported class load
 * @date: 2023/8/6 下午2:03
 */
public class ClassLoadTestCase {

  

    @Test
    public void partentFireTestCase() throws NoSuchMethodException, ClassNotFoundException, InvocationTargetException, IllegalAccessException, InstantiationException {
        ParentFireClassLoadTestCase parentFistClassLoad = new ParentFireClassLoadTestCase();
        Class testAClass = parentFistClassLoad.findClass("cn.jiayeli.riverMpas.tests.TestA");
        Class testBClass = parentFistClassLoad.findClass("cn.jiayeli.riverMpas.tests.TestB");
        Method aHello = testAClass.getDeclaredMethod("hello", String.class);
        Method helloB = testBClass.getDeclaredMethod("hello");
        aHello.invoke(testAClass.getConstructor().newInstance(), "args");
        helloB.setAccessible(true);
        helloB.invoke(testBClass.newInstance(), null);
    }
}


class ParentFireClassLoadTestCase extends ClassLoader{

    private Map<String, String> classPathMap = new HashMap<>();

    public ParentFireClassLoadTestCase() {
        classPathMap.put("cn.jiayeli.riverMpas.tests.TestA", "target/test-classes/cn/jiayeli/riverMpas/tests/TestA.class");
        classPathMap.put("cn.jiayeli.riverMpas.tests.TestB", "target/test-classes/cn/jiayeli/riverMpas/tests/TestB.class");
    }

    //在此类装入器的类路径上搜索并装入类, ru guo zhaobudao jiu diaoyong fulei jiazaiqijinxin jiazai
    @Override
    public Class<?> findClass(String name) throws ClassNotFoundException {
        String classPath = classPathMap.get(name);

        Function<String, Class<?>> classFind = classFileName -> {
            File classFile = new File(classFileName);
            Class<?> clazz = null;
            if (classFile.exists()) {
                try {
                    byte[] classBytes = Optional.ofNullable(getClassData.apply(classFile))
                            .orElseThrow(() -> new ClassNotFoundException());
                    clazz = defineClass(name, classBytes, 0, classBytes.length);
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
            return clazz;
        };

        return classFind.apply(classPath);
    }

    private Function<File, byte[]> getClassData = (File classFile)  -> {
        byte[] bytes = null;
        try(InputStream ins = new FileInputStream(classFile);
            ByteArrayOutputStream baos = new ByteArrayOutputStream()
        ) {
            byte[] buffer = new byte[4096];
            int read;
            while ((read = ins.read(buffer)) != -1) {
                baos.write(buffer, 0, read);
            }
            bytes = baos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return bytes == null ? new byte[]{} : bytes;
    };

}

output

TestA: cn.jiayeli.riverMpas.tests.ParentFireClassLoadTestCase@1e88b3c
TestC: jdk.internal.loader.ClassLoaders$AppClassLoader@6bc7c054
TestB: cn.jiayeli.riverMpas.tests.ParentFireClassLoadTestCase@1e88b3c

结论

TestA中依赖C,但是由于双亲委派的影响,A使用用户自定义类加载器加载,而C则由其父加载器进行了加载。

TestA

package cn.jiayeli.riverMpas.tests;

/**
 * @author: jiayeli.cn
 * @description
 * @date: 2023/8/6 下午3:53
 */
public class  TestA {
    public void hello() {
        System.out.println("TestA: " + this.getClass().getClassLoader());
    }

    public void hello(String arg1) {
        System.out.println("TestA: " + this.getClass().getClassLoader());
        TestC testC = new TestC();
        testC.hello();
    }
}

TestB

package cn.jiayeli.riverMpas.tests;

/**
 * @author: jiayeli.cn
 * @description
 * @date: 2023/8/6 下午4:02
 */
public class TestB {

    public void hello() {
        System.out.println("TestB: " + this.getClass().getClassLoader());
    }
}

TestC

package cn.jiayeli.riverMpas.tests;

/**
 * @author: jiayeli.cn
 * @description
 * @date: 2023/8/6 下午4:07
 */
public class TestC {

    public void hello() {
        System.out.println("TestC: " + this.getClass().getClassLoader());
    }

}

重写ClassLoader.loadClass方法打破双亲委派

demo

package cn.jiayeli.riverMpas.tests;

import org.junit.Test;

import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;

/**
 * @author: jiayeli.cn
 * @description multi version jas supported class load
 * @date: 2023/8/6 下午2:03
 */
public class ClassLoadTestCase {

    @Test
    public  void classLoadTestCase() {
        TestA testA = new TestA();
        TestB testb = new TestB();
        testA.hello();
        testb.hello();
        System.out.println("----------------------- default jdk classloader test -------------------------------\n");
    }

    @Test
    public void parentFistClassLoaderTestCase() throws NoSuchMethodException, ClassNotFoundException, InvocationTargetException, IllegalAccessException, InstantiationException {
        ParentFireClassLoadTestCase parentFistClassLoad = new ParentFireClassLoadTestCase();
        Class testAClass = parentFistClassLoad.findClass("cn.jiayeli.riverMpas.tests.TestA");
        Class testBClass = parentFistClassLoad.findClass("cn.jiayeli.riverMpas.tests.TestB");
        Method aHello = testAClass.getDeclaredMethod("hello", String.class);
        Method helloB = testBClass.getDeclaredMethod("hello");
        aHello.invoke(testAClass.getConstructor().newInstance(), "args");
        helloB.setAccessible(true);
        helloB.invoke(testBClass.newInstance(), null);
        System.out.println("----------------------- overwrite findClass function classloader test -------------------------------\n");
    }

    @Test
    public void customerClassLoaderTestCase() throws NoSuchMethodException, ClassNotFoundException, InvocationTargetException, IllegalAccessException, InstantiationException {
        ClassLoader parentClassLoad = Thread.currentThread().getContextClassLoader().getParent();
        CustomerClassLoader customerClassLoader = new CustomerClassLoader(parentClassLoad);
        Class testAClass = customerClassLoader.loadClass("cn.jiayeli.riverMpas.tests.TestA", false);
        Class testBClass = customerClassLoader.loadClass("cn.jiayeli.riverMpas.tests.TestB", false);
        Method helloA = testAClass.getDeclaredMethod("hello", String.class);
        Method helloB = testBClass.getDeclaredMethod("hello");
        helloA.invoke(testAClass.getConstructor().newInstance(), "args");
        helloB.invoke(testBClass.getConstructor().newInstance(), null);
        System.out.println("----------------------- overwrite loadClass function classloader test -------------------------------\n");
    }
}


class ParentFireClassLoadTestCase extends ClassLoader{

    private Map<String, String> classPathMap = new HashMap<>();

    public ParentFireClassLoadTestCase() {
        classPathMap.put("cn.jiayeli.riverMpas.tests.TestA", "target/test-classes/cn/jiayeli/riverMpas/tests/TestA.class");
        classPathMap.put("cn.jiayeli.riverMpas.tests.TestB", "target/test-classes/cn/jiayeli/riverMpas/tests/TestB.class");
    }

    //在此类装入器的类路径上搜索并装入类
    @Override
    public Class<?> findClass(String name) throws ClassNotFoundException {
        String classPath = classPathMap.get(name);

        Function<String, Class<?>> classFind = classFileName -> {
            File classFile = new File(classFileName);
            Class<?> clazz = null;
            if (classFile.exists()) {
                try {
                    byte[] classBytes = Optional.ofNullable(getClassData.apply(classFile))
                            .orElseThrow(() -> new ClassNotFoundException());
                    clazz = defineClass(name, classBytes, 0, classBytes.length);
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
            return clazz;
        };

        return classFind.apply(classPath);
    }

    public static Function<File, byte[]> getClassData = (File classFile)  -> {
        byte[] bytes = null;
        try(InputStream ins = new FileInputStream(classFile);
            ByteArrayOutputStream baos = new ByteArrayOutputStream()
        ) {
            byte[] buffer = new byte[4096];
            int read;
            while ((read = ins.read(buffer)) != -1) {
                baos.write(buffer, 0, read);
            }
            bytes = baos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return bytes == null ? new byte[]{} : bytes;
    };

}

class CustomerClassLoader extends ClassLoader{

    private ClassLoader jdkClassLoader;

    private Map<String, String> classPathMap = new HashMap<>();

    public CustomerClassLoader(ClassLoader jdkClassLoader) {
        this.jdkClassLoader = jdkClassLoader;
        classPathMap.put("cn.jiayeli.riverMpas.tests.TestA", "target/test-classes/cn/jiayeli/riverMpas/tests/TestA.class");
        classPathMap.put("cn.jiayeli.riverMpas.tests.TestB", "target/test-classes/cn/jiayeli/riverMpas/tests/TestB.class");
        // ClassA.hello("")方法需要ClassC,
        classPathMap.put("cn.jiayeli.riverMpas.tests.TestC", "target/test-classes/cn/jiayeli/riverMpas/tests/TestC.class");
    }

    //
    @Override
    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        AtomicReference<Exception> exception = new AtomicReference<>();
        Class<?> clazz = null;
        /*jdk 包里面的类使用JDK的类加载器加载*/
        try {
            clazz = jdkClassLoader.loadClass(name);
        } catch (ClassNotFoundException e) {
            System.out.println("jdk classLoad cannot loading class: " + name);
        }

        clazz = Optional.ofNullable(clazz)
            .orElseGet(() -> {
                File classFile = new File(classPathMap.get(name));
                Class findClazz = null;
                if (classFile.exists()) {
                    try {
                        byte[] classBytes = Optional.ofNullable(ParentFireClassLoadTestCase.getClassData.apply(classFile))
                                .orElseThrow(() ->  new ClassNotFoundException());
                        findClazz = defineClass(name, classBytes, 0, classBytes.length);
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                        exception.set(e);
                    }
                }
                return findClazz;
            });

        return Optional.ofNullable(clazz).orElseThrow(() -> (ClassNotFoundException) exception.get());
    }

}

output

TestA: cn.jiayeli.riverMpas.tests.CustomerClassLoader@5e5792a0
TestC: cn.jiayeli.riverMpas.tests.CustomerClassLoader@5e5792a0
TestB: cn.jiayeli.riverMpas.tests.CustomerClassLoader@5e5792a0
----------------------- overwrite loadClass function classloader test -------------------------------

TestA: jdk.internal.loader.ClassLoaders$AppClassLoader@6bc7c054
TestB: jdk.internal.loader.ClassLoaders$AppClassLoader@6bc7c054
----------------------- default jdk classloader test -------------------------------

TestA: cn.jiayeli.riverMpas.tests.ParentFireClassLoadTestCase@754ba872
TestC: jdk.internal.loader.ClassLoaders$AppClassLoader@6bc7c054
TestB: cn.jiayeli.riverMpas.tests.ParentFireClassLoadTestCase@754ba872
----------------------- overwrite findClass function classloader test -------------------------------

结论

重写loadClass方法的自定义类加载器进行类加载时目标加载类及其依赖的类都会由自定义加载器进行加载。

你可能感兴趣的:(java,java)