类加载器的相关问题(ClassLoader&&LinkageError)

本文探讨了在使用JUnit时遇到的类加载器问题,以及LinkageError的解决之道。通过对类加载机制的分析,文章揭示了柳暗花明的解决思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

下面的错误在google上搜了半天也没有特别满意的答案,后来看到一篇关于JUnit类加载器Bug的文章,总算觉得柳暗花明....
  

java.lang.LinkageError: Class org/w3c/dom/Document violates loader constraints at
java.lang.ClassLoader.defineClass0(Native Method) at
java.lang.ClassLoader.defineClass(ClassLoader.java:502) at
java.lang.ClassLoader.defineClass(ClassLoader.java:431) at
.......


这样的异常,主要是由于非系统 类加载器,即自定义的类加载器出现了问题,这个类加载器试图加载org.w3c.dom.Document ,此时如果类加载器没有进行正确的处理,或者Document 类不在JVM's system classpath下,类加载器便无法加载,自己加载不了,也不通知系统类加载器,上面的错误就出现啦。。。

可能理解有问题,可参照下面这篇文章,我试验了一下,完全可信:


Here's a patch for JUnit's busted classloader: junit-patch.jar.  And below is XmlTest.java, a regression test that breaks (with a java.lang.LinkageError) with the old classloader and the swingui/awtui TestRunners, but passes with the patch.

junit-patch.jar contains two classes (source included):

  1. ReloadableTestClassLoader - a properly delegating custom ClassLoader that currently relies on a System property value for its classpath: junit.test.path. The fix can be ported to junit.runner.TestCaseClassLoader or it can be effected through a new ClassLoader like this one.
  2. ReloadingTestSuiteLoader - modified to return this custom ClassLoader. If I can get this fix applied to the real JUnit, this class wouldn't necessarily need to be modified (it's only like 5 lines) because we could fix the original TestCaseClassLoader instead of writing a new one. Whatever; minor detail.

XmlTest.java:

import junit.framework.TestCase;
import javax.xml.parsers.*;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
    
public class XmlTest extends TestCase {

Document doc = null; public void testXml() throws ParserConfigurationException { doc = DocumentBuilderFactory.newInstance() .newDocumentBuilder() .newDocument(); // System.out.println("Hello! I changed again"); Element root = doc.createElement("xml-test"); assertNotNull(root); } }

This is a simple test that reproduces a LinkageError with the JUnit buggy ClassLoader under the following conditions:

  1. Use Sun JDK 1.4.x
  2. Edit JUnit's excluded.properties so that you comment out the exclusion of org.w3c.dom.* -- this is necessary to reveal the bug and is the easiest test I could think of. Make sure your edited excluded.properties is first in the classpath or updated into junit.jar at junit/runner/excluded.properties (BTW, obviously the new ReloadableTestClassLoader doesn't use or rely on excluded.properties at all).
  3. Put the DOM interfaces (in Xerces these are in xml-apis.jar) and an XML parser impl, such as Xerces, in your classpath to reproduce the bug. Why? Since you will have unexcluded org.w3c.dom.* in excluded.properties, JUnit will attempt to load those interfaces rather than delegate to the system loader. But they aren't in the scope of JUnit's buggy classloader if they aren't in the JVM's system classpath, so you have to add them. I.e., the 1.4.x JVM loads these in its boot loader, whose classpath JUnit's buggy loader doesn't know about.
  4. Example of command line to use to reproduce the bug (using junit.jar from JUnit 3.8.1, but any version should work): java -cp .;junit.jar;xml-apis.jar;xerces.jar junit.swingui.TestRunner XmlTest

You should see this in the runner's error display window:

 java.lang.LinkageError: Class org/w3c/dom/Document violates loader constraints at
java.lang.ClassLoader.defineClass0(Native Method) at 
java.lang.ClassLoader.defineClass(ClassLoader.java:502) at 
java.lang.ClassLoader.defineClass(ClassLoader.java:431) at 
junit.runner.TestCaseClassLoader.loadClass(TestCaseClassLoader.java:104) at 
java.lang.ClassLoader.loadClass(ClassLoader.java:255) at 
java.lang.ClassLoader.loadClassInternal(ClassLoader.java:315) at 
XmlTest.testXml(XmlTest.java:16) at 
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at 
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at 
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at 
java.lang.reflect.Method.invoke(Method.java:324) at 
junit.framework.TestCase.runTest(TestCase.java:154) at 
junit.framework.TestCase.runBare(TestCase.java:127) at 
junit.framework.TestResult$1.protect(TestResult.java:106) at 
junit.framework.TestResult.runProtected(TestResult.java:124) at 
junit.framework.TestResult.run(TestResult.java:109) at 
junit.framework.TestCase.run(TestCase.java:118) at 
junit.framework.TestSuite.runTest(TestSuite.java:208) at 
junit.framework.TestSuite.run(TestSuite.java:203) at 
junit.swingui.TestRunner$16.run(TestRunner.java:623)

Now run the same test with the fixed ClassLoader:

  1. shadow classes in junit.jar by listing junit-patch.jar first in the classpath
  2. set junit.test.path property to include the path to XmlTest.class
  3. run XmlTest with the Swing or AWT test runner:
    java -cp junit-patch.jar;junit.jar -Djunit.test.path=. junit.swingui.TestRunner XmlTest
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值