前言
呵呵 最近碰到一些 老项目, 使用的传统的 libs 文件夹管理依赖, 显然存在的问题就是 jar 的依赖的问题
然后 之前呢, 也碰到了一些 依赖的相关问题
1. 异常:Class net.sf.cglib.core.DebuggingClassWriter overrides final method visit
2. tomcat启动内存堆栈溢出ASN1EncodableVector,DEREncodableVector循环依赖
我们这里来看的问题便是 "tomcat启动内存堆栈溢出ASN1EncodableVector,DEREncodableVector循环依赖"
本文会使用到 classpath中存在多个jar存在同限定名的class classloader会如何加载 相关的知识, 下文会提及放出链接
以下调试 java 层面的调试基于 jdk8
测试用例
新建一个 web application, 创建 pom.xml, 新建 HelloWorldServlet, 然后配置 tomcat, 配置 artificates 等等相关事项, 这里不赘述
当然以下用例 和 我们这里核心要描述的东西没有必要的关联, 以下用例只是为了测试 web application 启动正常
package com.hx.test;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* HelloWorldServlet
*
* @author Jerry.X.He
* @version 1.0
* @date 2021-05-22 11:11
*/
@WebServlet("/hello/world")
public class HelloWorldServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println(" do get ");
resp.getOutputStream().println(" do get ");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println(" do post ");
resp.getOutputStream().println(" do post ");
}
}
pom.xml 文件内容如下
如下两个依赖 才是导致这里的问题的核心的东西
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.hx</groupId>
<artifactId>HelloWebApplication</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<HXLog.version>0.0.2</HXLog.version>
</properties>
<dependencies>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk14</artifactId>
<version>1.38</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.52</version>
</dependency>
</dependencies>
</project>
然后启动项目, 偶尔会报错如下, 但是 偶尔的情况下 又不会报错
呵呵 所以 我们这里所需要明确的问题有两个
1. 为什么会出现这个循环依赖的报错 ?
2. 为什么是偶尔报错 ?
30-May-2021 09:36:02.767 WARNING [RMI TCP Connection(2)-127.0.0.1] org.apache.tomcat.util.descriptor.web.WebXml.setVersion Unknown version string [4.0]. Default version will be used.
30-May-2021 09:36:12.474 SEVERE [RMI TCP Connection(2)-127.0.0.1] org.apache.catalina.core.ContainerBase.addChildInternal ContainerBase.addChild: start:
org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[]]
at org.apache.catalina.util.LifecycleBase.handleSubClassException(LifecycleBase.java:440)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:198)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:743)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:719)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:691)
at org.apache.catalina.startup.HostConfig.manageApp(HostConfig.java:1767)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:286)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:483)
at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:431)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:286)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
at com.sun.jmx.remote.security.MBeanServerAccessController.invoke(MBeanServerAccessController.java:468)
at javax.management.remote.rmi.RMIConnectionImpl.doOperation(RMIConnectionImpl.java:1468)
at javax.management.remote.rmi.RMIConnectionImpl.access$300(RMIConnectionImpl.java:76)
at javax.management.remote.rmi.RMIConnectionImpl$PrivilegedOperation.run(RMIConnectionImpl.java:1309)
at java.security.AccessController.doPrivileged(Native Method)
at javax.management.remote.rmi.RMIConnectionImpl.doPrivilegedOperation(RMIConnectionImpl.java:1408)
at javax.management.remote.rmi.RMIConnectionImpl.invoke(RMIConnectionImpl.java:829)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:357)
at sun.rmi.transport.Transport$1.run(Transport.java:200)
at sun.rmi.transport.Transport$1.run(Transport.java:197)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Transport.java:196)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:573)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:834)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:688)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:687)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.IllegalStateException: Unable to complete the scan for annotations for web application [] due to a StackOverflowError. Possible root causes include a too low setting for -Xss and illegal cyclic inheritance dependencies. The class hierarchy being processed was [org.bouncycastle.asn1.ASN1EncodableVector->org.bouncycastle.asn1.DEREncodableVector->org.bouncycastle.asn1.ASN1EncodableVector]
at org.apache.catalina.startup.ContextConfig.checkHandlesTypes(ContextConfig.java:2071)
at org.apache.catalina.startup.ContextConfig.processAnnotationsStream(ContextConfig.java:2009)
at org.apache.catalina.startup.ContextConfig.processAnnotationsJar(ContextConfig.java:1961)
at org.apache.catalina.startup.ContextConfig.processAnnotationsUrl(ContextConfig.java:1931)
at org.apache.catalina.startup.ContextConfig.processAnnotations(ContextConfig.java:1887)
at org.apache.catalina.startup.ContextConfig.processClasses(ContextConfig.java:1186)
at org.apache.catalina.startup.ContextConfig.webConfig(ContextConfig.java:1093)
at org.apache.catalina.startup.ContextConfig.configureStart(ContextConfig.java:779)
at org.apache.catalina.startup.ContextConfig.lifecycleEvent(ContextConfig.java:299)
at org.apache.catalina.util.LifecycleBase.fireLifecycleEvent(LifecycleBase.java:123)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5066)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
... 44 more
30-May-2021 09:36:12.478 SEVERE [RMI TCP Connection(2)-127.0.0.1] org.apache.tomcat.util.modeler.BaseModelMBean.invoke Exception invoking method manageApp
java.lang.IllegalStateException: ContainerBase.addChild: start: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[]]
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:747)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:719)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:691)
at org.apache.catalina.startup.HostConfig.manageApp(HostConfig.java:1767)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:286)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:483)
at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:431)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:286)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
at com.sun.jmx.remote.security.MBeanServerAccessController.invoke(MBeanServerAccessController.java:468)
at javax.management.remote.rmi.RMIConnectionImpl.doOperation(RMIConnectionImpl.java:1468)
at javax.management.remote.rmi.RMIConnectionImpl.access$300(RMIConnectionImpl.java:76)
at javax.management.remote.rmi.RMIConnectionImpl$PrivilegedOperation.run(RMIConnectionImpl.java:1309)
at java.security.AccessController.doPrivileged(Native Method)
at javax.management.remote.rmi.RMIConnectionImpl.doPrivilegedOperation(RMIConnectionImpl.java:1408)
at javax.management.remote.rmi.RMIConnectionImpl.invoke(RMIConnectionImpl.java:829)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:357)
at sun.rmi.transport.Transport$1.run(Transport.java:200)
at sun.rmi.transport.Transport$1.run(Transport.java:197)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Transport.java:196)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:573)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:834)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:688)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:687)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
30-May-2021 09:36:12.479 SEVERE [RMI TCP Connection(2)-127.0.0.1] org.apache.tomcat.util.modeler.BaseModelMBean.invoke Exception invoking method createStandardContext
javax.management.RuntimeOperationsException: Exception invoking method manageApp
at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:294)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:483)
at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:431)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:286)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
at com.sun.jmx.remote.security.MBeanServerAccessController.invoke(MBeanServerAccessController.java:468)
at javax.management.remote.rmi.RMIConnectionImpl.doOperation(RMIConnectionImpl.java:1468)
at javax.management.remote.rmi.RMIConnectionImpl.access$300(RMIConnectionImpl.java:76)
at javax.management.remote.rmi.RMIConnectionImpl$PrivilegedOperation.run(RMIConnectionImpl.java:1309)
at java.security.AccessController.doPrivileged(Native Method)
at javax.management.remote.rmi.RMIConnectionImpl.doPrivilegedOperation(RMIConnectionImpl.java:1408)
at javax.management.remote.rmi.RMIConnectionImpl.invoke(RMIConnectionImpl.java:829)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:357)
at sun.rmi.transport.Transport$1.run(Transport.java:200)
at sun.rmi.transport.Transport$1.run(Transport.java:197)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Transport.java:196)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:573)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:834)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:688)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:687)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.IllegalStateException: ContainerBase.addChild: start: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[]]
[2021-05-30 09:36:12,495] Artifact HelloWebService:war exploded: Error during artifact deployment. See server log for details.
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:747)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:719)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:691)
at org.apache.catalina.startup.HostConfig.manageApp(HostConfig.java:1767)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:286)
... 35 more
30-May-2021 09:36:12.572 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/Library/ProgramFiles/apache-tomcat-8.5.66/webapps/manager]
30-May-2021 09:36:12.719 INFO [localhost-startStop-1] org.apache.jasper.servlet.TldScanner.scanJars At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
30-May-2021 09:36:12.757 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/Library/ProgramFiles/apache-tomcat-8.5.66/webapps/manager] has finished in [186] ms
当报错的情况下, 访问我们提供的 /hello/world 的请求, 会报告 404, 并且 当前项目的服务均不可用
没报错的情况下, 就是当前项目的服务都正常可用
1. 为什么会出现这个循环依赖的报错 ?
我们根据堆栈信息找到出现 StackOverflow 的地方, 我们这里上下文的 cacheEntry 表示的 org.bouncycastle.asn1.ASN1EncodableVector 相关的信息[图上体现不出来, 但是上一个栈帧可以看到, 这里不多截图]
然后从这里可以看到 org.bouncycastle.asn1.ASN1EncodableVector 的基类是 org.bouncycastle.asn1.DEREncodableVector, 然后 我们继续往下跟
我们继续往下看, org.bouncycastle.asn1.ASN1EncodableVector 的基类是 org.bouncycastle.asn1.DEREncodableVector, 然后上面的处理会递归处理 org.bouncycastle.asn1.DEREncodableVector
因此 这个栈帧截图, cacheEntry 对应的是 org.bouncycastle.asn1.DEREncodableVector 的关联的信息
然后我们看一下 ??, 擦 为什么 org.bouncycastle.asn1.DEREncodableVector 的基类又是 org.bouncycastle.asn1.ASN1EncodableVector 这是什么情况 ??
1. 这是第一点, 这两个类在一定的情况下 为什么相互继承了对方?
2. 第二点是 这个无限递归的构成, 从如下代码可以看到, populateSCIsForCacheEntry 的一个必要条件是 cacheEntry.sciSet == null, 再看一点 sciSet 的设置是在 方法的末尾, 处理完成了之后 才设置
因此, 这就构成了程序代码上的无限递归, ASN1EncodableVector 初始化的时候发现基类是 DEREncodableVector, 并且 DEREncodableVector 的 sciSet 为 null
开始初始化 DEREncodableVector, 初始化的时候发现基类是 ASN1EncodableVector, 并且 ASN1EncodableVector 的 sciSet 为 null, 又来处理 ASN1EncodableVector
开始初始化 ASN1EncodableVector, 初始化的时候发现基类是 DEREncodableVector, 并且 DEREncodableVector 的 sciSet 为 null, 又来处理 DEREncodableVector
...... 省略 n 个步骤
这两个类在一定的情况下 为什么相互继承了对方?
我们搜索一下 org.bouncycastle.asn1.ASN1EncodableVector 和 org.bouncycastle.asn1.DEREncodableVector
会发现我们添加的两个依赖里面都有 org.bouncycastle.asn1.ASN1EncodableVector 和 org.bouncycastle.asn1.DEREncodableVector
那么我们来看一下 org.bouncycastle.asn1.ASN1EncodableVector 和 org.bouncycastle.asn1.DEREncodableVector 在这两个不同的包里面的细节
在 bcprov-jdk14-1.38 版本中 org.bouncycastle.asn1.ASN1EncodableVector 继承自 org.bouncycastle.asn1.DEREncodableVector
在 bcprov-jdk14-1.38 版本中 org.bouncycastle.asn1.DEREncodableVector 继承自 java.lang.Object
在 bcprov-jdk15on-1.52 版本中 这两个类的情况是反过来的
org.bouncycastle.asn1.ASN1EncodableVector 是继承自 java.lang.Object
bcprov-jdk15on-1.52 版本中 org.bouncycastle.asn1.DEREncodableVector 继承自 org.bouncycastle.asn1.ASN1EncodableVector
从上面的运行时调试情况来说, 我们可以推导 上面加载的 org.bouncycastle.asn1.ASN1EncodableVector 应该是来自于 bcprov-jdk14-1.38.jar
然后 加载的 org.bouncycastle.asn1.DEREncodableVector 应该是来自于 bcprov-jdk15on-1.52.jar
然后 才出现了这个我们逻辑上看到的两个 类相互继承的情况
我们来看一下 加载的 org.bouncycastle.asn1.ASN1EncodableVector 的情况
可以看出, 果然是来自于 bcprov-jdk14-1.38.jar
再来看一下 加载 org.bouncycastle.asn1.DEREncodableVector 的情况
来自于 javaClassCache, 那么这个 javaClassCache 是从哪里添加的 org.bouncycastle.asn1.DEREncodableVector 呢
是在 populateSCIsForCacheEntry 再之前的一个步骤, populateJavaClassCache, 不过我们这里核心关注如何加载的 org.bouncycastle.asn1.DEREncodableVector
在 WebappClassloader 里面加载 org.bouncycastle.asn1.DEREncodableVector, 从 classpath 依次选择, 两个 jar 中都有 org.bouncycastle.asn1.DEREncodableVector, 因此这里选择了靠前的 bcprov-jdk15on-1.52.jar
因此关于 循环依赖的这个情况, 大致就是这样了
2. 为什么是偶尔报错 ?
首先我们需要梳理一下 那些地方可能会造成顺序出现不固定 ?
1. 就是我们上面 processAnnotationsJar 遍历的 jar 的顺序
2. 就是我们 context.getLoader().getClassLoader().getResourceAsStream(name) 加载类库用到的 jar
我们上面 processAnnotationsJar 遍历的 jar 的顺序
经过多次测试 我们这里的额 fragments 的顺序确实有些时候会不一样
但是看一个细节 fragments 是 LinkedHashSet, 期望应该是保持一定的顺序阿?, 为什么会出现这种情况
当然追查这个原因还是花费了不少时间, 呵呵 这里直接进入重点
这个 otherSet 是一个普通的 HashSet, 并且元素类型为 WebXml, 并且没有重写 hashCode, equals, 因此 偶尔会导致 otherSet 遍历的顺序不固定, 以至于 后面添加进入 orderedFragments 中这两个 jar 的顺序不固定
从这里的代码, 上下文语义 这里的 orderedFragments 我更倾向于是 jar 和 container fragments 这两大类存在顺序
再重试一次 这里 otherSet 中元素顺序又不一致, 导致后面的 orderedFragments 中的元素的顺序的不确定
context.getLoader().getClassLoader().getResourceAsStream(name) 加载类库用到的 jar
从实际的测试得知, 每次 context.getLoader().getClassLoader().getResourceAsStream(name) 里面得到的 org.bouncycastle.asn1.DEREncodableVector 总是 bcprov-jdk15on-1.52.jar 的 jar
我们来看下为什么 ?
这里是注册 jar 到 StandardRoot 里面 allResources[3] [等价于jarResources] 的地方, 我们看一下 外面的获取列表的地方
外层的 jar 列表来自于 listResources("WEB-INF/lib"), 加载的 WEB-INF/lib 下面的各种资源[preResource, mainResource, classResource, jarResources, postResources], 我们这里 着重关注 jarResources
最终使用的是 File.list 来加载文件列表
看 File.list 文档介绍的是不保证顺序, 但是多次测试 返回的 列表都是一致的, 因此我们假设 StandardRoot 中的 jarResources 的顺序一致
context.getLoader().getClassLoader().getResourceAsStream(name) 加载类库用到的 jar, 每次均使用 bcprov-jdk15on-1.52.jar
完
参考
tomcat启动内存堆栈溢出ASN1EncodableVector,DEREncodableVector循环依赖
Jenkins自动部署到Tomcat随机失败的问题
classpath中存在多个jar存在同限定名的class classloader会如何加载