Java how does a heterogeneous loader use classes that have been loaded by other classloaders?

ask the bosses:

class An and class B are loaded by their respective classloaders, and it is known that A loads first and B then loads.
class C and B are in the same jar and loaded by the same class loader.
when I call a method of An in B and the parameter is a new instance of C, I report to ClassNotFoundException C. what should I do? Thank you!

add that the parameters of the method An I called in B are of type Object , and An itself is completely unaware of the existence of C.

after actual verification, it is found that A"s ClassLoader is the parent loader of B"s ClassLoder.


The answer to

@ chenshun can be referred to, but the conclusion is wrong.
first calls a method of An in B that ClassALoader is the parent loader of ClassBLoader , thus ruling out the second possibility of @ chenshun answer. And the first possibility really won't make a mistake. But there is also the possibility of reporting errors, such as explicitly loading ClassC in ClassA , so I suspect this is the problem with the subject. The real reason depends on the code.

Demo verify:

/**
 * Class D:\temp\a\segmentfault\Question1010000017720845
 */
public class ClassA {

    public void method(ClassC c) {
        System.out.println("ClassA.method()");
        System.out.println(c);
        //ClassBClassC segmentfault.Question1010000017720845.MyClassLoader_2@23fc625e
        System.out.println(c.getClass().getClassLoader());
        //ClassAClassC classC not found
        try {
            Class classC = this.getClass().getClassLoader().loadClass("segmentfault.Question1010000017720845.ClassC");
        } catch (ClassNotFoundException e) {
            System.out.println("classC not found");
        }

    }
}

/**
 * Class D:\temp\b\segmentfault\Question1010000017720845
 */
public class ClassB {
    public void method(){
        System.out.println("ClassB...");
        ClassA a = new ClassA();
        //ClassC not found ClassC
        ClassC c = new ClassC();
        //ClassAClassA
        //ClassAClassB
        a.method(c);
    }
}

/**
 * Class D:\temp\b\segmentfault\Question1010000017720845
 */
public class ClassC {
    public void method(){
        System.out.println("ClassC.method()");
    }
}


/**
 * ClassLoaderDemo.java
 * :ClassA,ClassB,ClassCclasspath
 */
class MyClassLoader_1 extends ClassLoader {

    public MyClassLoader_1(ClassLoader parent) {
        super(parent);
    }

    @Override
    protected Class<?> findClass(String name) {
        String myPath = "file:/D:/temp/a/" + name.replace(".","/") + ".class";
        System.out.println("classLoader_1 :" + myPath);
        byte[] cLassBytes = null;
        Path path = null;
        try {
            path = Paths.get(new URI(myPath));
            cLassBytes = Files.readAllBytes(path);
        } catch (IOException | URISyntaxException e) {
//            e.printStackTrace();
            System.out.println("");
            return null;
        }
        Class clazz = defineClass(name, cLassBytes, 0, cLassBytes.length);
        return clazz;
    }
}

/**
 * ClassBClassC
 */
class MyClassLoader_2 extends ClassLoader {
    public MyClassLoader_2(ClassLoader parent) {
        super(parent);
    }

    @Override
    protected Class<?> findClass(String name) {
        String myPath = "file:/D:/temp/b/" + name.replace(".","/") + ".class";
        System.out.println("classLoader_2 :" + myPath);
        byte[] cLassBytes = null;
        Path path = null;
        try {
            path = Paths.get(new URI(myPath));
            cLassBytes = Files.readAllBytes(path);
        } catch (IOException | URISyntaxException e) {
//            e.printStackTrace();
            System.out.println("");
            return null;
        }
        Class clazz = defineClass(name, cLassBytes, 0, cLassBytes.length);
        return clazz;
    }
}

public class ClassLoaderDemo {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        MyClassLoader_1 classLoader_1 = new MyClassLoader_1(ClassLoaderDemo.class.getClassLoader());
        MyClassLoader_2 classLoader_2 = new MyClassLoader_2(classLoader_1);
        Class classB = classLoader_2.loadClass("segmentfault.Question1010000017720845.ClassB");
        Object classBInstance = classB.newInstance();
        Method method = classB.getMethod("method");
        method.invoke(classBInstance);
    }

}

since the classes (a / b) loaded by their respective class loaders can access each other, the two loaders should be interoperable. So is it true that c cannot be accessed outside the jar package in the first place?


first of all, thank you for your answers and discussions. @ Linfeng's answer is closest to the actual situation.
my solution is as follows:

Class B:

static {
        try {
            Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
            method.setAccessible(true);
            URL url = B.class.getProtectionDomain().getCodeSource().getLocation();// BC  jar
            method.invoke(A.class.getClassLoader(), url);//  A  BC.jar 
            // (B) Class C
            Class<?> clazz = Class.forName("C", true, A.class.getClassLoader());
            System.out.println(clazz);
            A.method(clazz.getConstructor().newInstance());
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

take a typical tomcat as an example:
class An is in xxa.jar
class B and Class C are in xxbc.jar

whether A comes first or B comes first, there must be a reference that caused classloader to load this class. You are now calling a method of An in b. Here are several cases

class An and class B are loaded by their respective classloaders, and it is known that A loads first and B then loads. Class C and B are in the same jar and loaded by the same class loader. When I call a method of An in B and the parameter is a new instance of C, I report to ClassNotFoundException C

1, An is loaded by commonClassloader, and bmax c is loaded by webappClassloader. Obviously, classNotFoundException will be thrown because the data in the current class can only be loaded by the class loader that loads the class.

2, An is loaded by webappClassloader, b is loaded by commonClassloader, b calls a method of A, and the argument is an instance of C, and A not found is exposed, unless A.

is instantiated using the context loader.

if you want to put the picture under your comment, you can't put it. Just send it here

clipboard.png

Springbootstartertomcat/lib(8.5.35)Springcomponent-scan@ConfigurationBeanFactoryPostProcessorbeanAB/C jar


@ .

@

webappwebinf/libClasscommon

java

:tomcat8.5.35() Spring 5.0.7.RELEASE

clipboard.png

clipboard.png

clipboard.png

asm bytecode

{
            mv = cw.visitMethod(ACC_PUBLIC, "googo", "(Lorg/springframework/boot/ApplicationArguments;)Ljava/lang/Object;", null, null);
            mv.visitCode();
            Label l0 = new Label();
            mv.visitLabel(l0);
            mv.visitLineNumber(42, l0);
            mv.visitInsn(ICONST_1);
            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false);
            mv.visitInsn(ARETURN);
            Label l1 = new Label();
            mv.visitLabel(l1);
            mv.visitLocalVariable("this", "Lboot/raycloud/project/monitor/common/MonitorAutoConfigure;", null, l0, l1, 0);
            mv.visitLocalVariable("applicationArguments", "Lorg/springframework/boot/ApplicationArguments;", null, l0, l1, 1);
            mv.visitMaxs(1, 2);
            mv.visitEnd();
        }

@ Code Universe just get some real talent.

Menu