Tomcat启动抛出StackOverflowError:bouncycastle类循环继承问题咨询
问题根因
你预设的「Tomcat会先完整解析单个jar包内的所有类再处理下一个jar」的前提不成立,Tomcat的SCI类扫描是深度优先递归处理单个类的继承链,处理过程中会跨jar加载父类,加载结果完全由类加载器的类路径优先级决定,具体触发流程如下:
- 扫描触发:首先扫描到
bcprov-jdk15:1.45版本的ASN1Boolean类,调用三参数重载的populateJavaClassCache存入缓存,该缓存条目记录的父类为DERBoolean(来自1.45版本的类定义)。 - 父类加载:接下来递归处理
ASN1Boolean的父类DERBoolean,调用单参数重载的populateJavaClassCache加载该类。如果此时类路径中bcprov-jdk15on:1.60的优先级更高,类加载器返回的是1.60版本的DERBoolean字节码。 - 循环映射生成:1.60版本的
DERBoolean被解析后存入缓存,该条目记录的父类是ASN1Boolean(来自1.60版本的类定义)。
此时缓存中已经形成了完整的循环继承映射:
org.bouncycastle.asn1.ASN1Boolean→ 父类org.bouncycastle.asn1.DERBooleanorg.bouncycastle.asn1.DERBoolean→ 父类org.bouncycastle.asn1.ASN1Boolean
- 栈溢出触发:
populateSCIsForCacheEntry方法的核心逻辑是递归遍历类的父类和接口链,匹配@HandlesTypes注解声明的需要处理的类型,遍历到这两个类时直接进入死递归,最终抛出StackOverflowError。
之前的推导误区说明
你认为缓存不会重复存入同全类名的类确实没错,两个类的缓存条目都不会被覆盖,但两个条目分别来自不同版本的类定义,各自的父类指向刚好形成闭环,和你假设的「先全量加载完单个jar所有类再处理下一个jar」的执行顺序完全不一致,因此你预判的「缓存中不存在循环映射」的结论不成立。
内容的提问来源于stack exchange,提问作者Jadic
相关产品推荐
相关产品推荐

