一个Tomcat类加载问题
背景 一个Tomcat实例中运行了三个应用,其中一个对接了Apereo的CAS系统。现在要求另外两个系统也对接CAS系统,问题就出现了: 应用启动后打开其中两个应用的任何一个,登录完成后系统都没有问题。唯独首选打开第三个,其他两个报错ClassNotFoundException: org.apache.xerces.parsers.SAXParser。 发现这个类来自xerces:xercesImpl:jar:2.6.2,使用mvn dependency:tree发现是被xom:xom:1.1简洁引用。 分析 CAS client jar中使用XMLReaderFactory创建XMLReader,首次创建会从classpath中查找META-INF/services/org.xml.sax.driver文件,这个文件里的内容是一个类的全名。比如xercesImpl中该文件的内容是org.apache.xerces.parsers.SAXParser。 找到之后会将类名保存在XMLReaderFactory的静态变量_clsFromJar,并标记不会再查找org.xml.sax.driver文件。找不到的话则使用com.sun.org.apache.xerces.internal.parsers.SAXParser类。 然后再使用当前线程的ContextClassLoader对类进行加载,这里的的ContextClassLoader是一个WebAppClassLoader的实例。 同时XMLReaderFactory类是被BootStrapClassLoader加载的,为三个应用共享。 Tomcat类记载机制 Tomcat中有四个位置可以存放Java类库:/commons、/server、/shared和各Web应用的WEB-INF/lib目录。 /commons目录中的类库可以被Tomcat和所有Web应用使用 /server目录中的类库只能被Tomcat使用 /shared目录中的可以被所有Web应用的使用,但是对Tomcat不可见 各Web应用的WEB-INF/lib目录中的类库则只能被该的应用使用 Tomcat的使用CommonClassLoader、CatalinaClassLoader、SharedClassLoader、WebAPPClassLoader加载对应目录中的类库。 Bootstrap、Extension、Application是虚拟机使用的系统类加载器。 类的加载使用双亲委派机制(Parent-Delegation)。 Bootstrap | Extension | Application | System | Common / \ Catalina Shared / \ WebApp1 ... WebApp2 | | Jasper Jasper 解决方案 在另外两个应用中添加xerces:xercesImpl:jar:2.