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
Springbootstartertomcat/lib(8.5.35)Springcomponent-scan@ConfigurationBeanFactoryPostProcessorbeanAB/C jar
@ .
@
webappwebinf/libClasscommon
java
:tomcat8.5.35() Spring 5.0.7.RELEASE
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.