Java 1.8+mockejb初始化InitialContext遇ExceptionInInitializerError
解决MockEJB 0.6-beta2与Java 8兼容问题及替代方案
问题概述
使用MockEJB 0.6-beta2搭配Java 8搭建EJB测试项目时,执行ctx = new InitialContext();代码行抛出ExceptionInInitializerError,核心原因是MockEJB依赖的旧版CGLib无法适配Java 8的模块反射访问限制。
完整错误栈
java.lang.ExceptionInInitializerError at net.sf.cglib.core.KeyFactory$Generator.generateClass(KeyFactory.java:166) at net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25) at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:216) at net.sf.cglib.core.KeyFactory$Generator.create(KeyFactory.java:144) at net.sf.cglib.core.KeyFactory.create(KeyFactory.java:116) at net.sf.cglib.core.KeyFactory.create(KeyFactory.java:108) at net.sf.cglib.core.KeyFactory.create(KeyFactory.java:104) at net.sf.cglib.proxy.Enhancer.<clinit>(Enhancer.java:69) at org.mockejb.interceptor.InterceptableProxy.create(InterceptableProxy.java:38) at org.mockejb.jndi.MockContextFactory.getInitialContext(MockContextFactory.java:47) at java.naming/javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:732) at java.naming/javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:305) at java.naming/javax.naming.InitialContext.init(InitialContext.java:236) at java.naming/javax.naming.InitialContext.<init>(InitialContext.java:184) at ar.com.clarin.suscripciones.servicios.impl.GestionarClienteComercialShould.setUp(GestionarClienteComercialShould.java:31) at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) at java.base/java.lang.reflect.Method.invoke(Method.java:577) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:93) at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:40) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:529) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:756) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:452) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:210) Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @cd2dae5 at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354) at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297) at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:200) at java.base/java.lang.reflect.Method.setAccessible(Method.java:194) at net.sf.cglib.core.ReflectUtils$2.run(ReflectUtils.java:56) at java.base/java.security.AccessController.doPrivileged(AccessController.java:318) at net.sf.cglib.core.ReflectUtils.<clinit>(ReflectUtils.java:46) ... 36 more
测试代码
public class GestionarClienteComercialShould { private Context ctx; @Before public void setUp() throws Exception { // Setup the JNDI environment to use the MockEJB // context factory MockContextFactory.setAsInitial();; // Create the initial context that will be used for binding EJBs ctx = new InitialContext(); } @Test public void test() throws NamingException { // Create an instance of the MockContainer MockContainer mc = new MockContainer(ctx); GestionarBPLocal gestionarBp = mock(GestionarBP.class); SessionBeanDescriptor dd = new SessionBeanDescriptor( "java:comp/env/ejb/GestionarBP", GestionarBPLocal.class, GestionarBP.class, gestionarBp) ; // Deploy our bean to the container allowing it to // be found via JNDI and tested mc.deploy(dd); } }
POM依赖配置
<dependencies> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.26</version> </dependency> <!-- Test --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-inline</artifactId> <version>3.4.0</version> <scope>test</scope> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.26</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockejb</groupId> <artifactId>mockejb</artifactId> <version>0.6-beta2</version> <scope>test</scope> </dependency> </dependencies>
解决方案
临时修复MockEJB兼容问题
MockEJB 0.6-beta2依赖的CGLib版本过旧,可通过以下步骤修复:
- 在POM中添加兼容Java 8的CGLib依赖,覆盖旧版本:
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> <scope>test</scope> </dependency>
- 运行测试时添加JVM参数,开放模块访问权限:
--add-opens java.base/java.lang=ALL-UNNAMED
推荐替代EJB Mock库
MockEJB已多年未维护,推荐使用以下现代兼容方案:
1. EJB3Mock
专注EJB 3.x的轻量Mock库,支持JNDI与EJB容器模拟,API简洁,兼容Java 8+。
POM依赖:
<dependency> <groupId>org.ejb3unit</groupId> <artifactId>ejb3unit-core</artifactId> <version>0.9.8</version> <scope>test</scope> </dependency>
2. Arquillian + Mockito
容器化测试框架,可配合Mockito模拟EJB依赖,支持真实/嵌入式容器,适配复杂EJB场景。
核心依赖:
<dependency> <groupId>org.jboss.arquillian.junit</groupId> <artifactId>arquillian-junit-container</artifactId> <version>1.7.0.Final</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>4.11.0</version> <scope>test</scope> </dependency>
3. OpenEJB Embedded
使用嵌入式OpenEJB容器运行测试,无需Mock,适合需要真实EJB环境的场景,兼容Java 8+。
POM依赖:
<dependency> <groupId>org.apache.openejb</groupId> <artifactId>openejb-core</artifactId> <version>4.7.5</version> <scope>test</scope> </dependency>
内容的提问来源于stack exchange,提问作者gerumato
相关产品推荐
相关产品推荐

