就以典型的tomcat为例说明:
class A 位于xxa.jar中
class B 和 Class C 位于 xxbc.jar中
不论A先还是B先,肯定都是存在引用导致了classloader去加载了这个类,你现在是b中调用A的一个方法,以下是几种情况
class A 和 class B 是由各自的类加载器加载的,已知A先加载,B后加载。class C 与 B 在同一jar内,同一个类加载器加载。当我在B中调用A的一个方法,参数是C的新实例,就报ClassNotFoundException C
1、A被commonClassloader加载,b/c被webappClassloader加载,很显然,会抛出classNotFoundException,具体原因就是当前类中的数据只能由加载该类的类加载器加载。
2、A被webappClassloader加载,b/c被commonClassloader加载,b调用A的一个方法,参数是C的实例,这时爆出A not found,除非使用上下文加载器实例化A.
想在你的评论下放图的,放不了。就发这里了
该图是我用Springboot的一个starter放置于tomcat/lib目录下(8.5.35版本),然后用Spring的component-scan扫描,通过@Configuration注册,随后通过我自定义的BeanFactoryPostProcessor修改bean信息,这个可以模拟A和B/C jar包的问题吧。
@Configuration
@ConditionalOnClass(name = {"org.springframework.beans.factory.config.BeanFactoryPostProcessor"})
public class MonitorAutoConfigure {
@Bean
public static BeanFactoryPostProcessor my() {
String lowerCase = MonitorAutoConfigure.class.getClassLoader().toString().toLowerCase();
System.out.println(lowerCase + "\tchen");
return new MyBeanFactoryPostProcessor(lowerCase.contains("webappclassloader") && !lowerCase.startsWith("org.springframework.boot"));
}
}
@临风 代码就只能放这么点了,涉及隐私,不好意思.
@临风 不好意思,没找到你的邮箱
先由webapp加载器将webinf/lib下的Class加载好,并实例化,然后传给由父加载器(common)加载的类的实例方法
这句话其实本身就是错误的,因为还没有等到你传递的过程,类加载的过程就已经失败了,具体原因就不说了,在添张图吧,具体的原因深入了解java虚拟机已经描述了。
环境:tomcat8.5.35(非插件) 启动Spring 5.0.7.RELEASE
报错图
实际代码
asm字节码
{
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();
}
@代码宇宙 喷人就拿点真才实学。