Spring AOP中创建id为proxy的Bean报错,请求排查解决
我基于Spring 3.0实现了一套AOP代码,包含以下文件:
applicatonContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="obj" class="com.learningspring.aop.aopA"/> <bean id="beforeAdvisor" class="com.learningspring.aop.BeforeAdvisor"/> <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="obj"/> <property name="interceptorNames"> <list> <value>beforeAdvisor</value> </list> </property> </bean> </beans>
aopA.java
package com.learningspring.aop; public class aopA { public void m() { System.out.println("Actual business logic"); } }
BeforeAdvisor.java
package com.learningspring.aop; import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice; public class BeforeAdvisor implements MethodBeforeAdvice { @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("Additional concern before actual logic "); } }
TestAOP.java
package com.learningspring.aop; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; public class TestAOP { public static void main(String[] args) { Resource r = new ClassPathResource("applicationContext.xml"); BeanFactory bf = new XmlBeanFactory(r); aopA a = (aopA) bf.getBean("proxy"); a.m(); } }
运行测试类时,出现创建proxy Bean失败的错误,错误信息如下:
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'proxy': FactoryBean threw exception on object creation; nested exception is java.lang.ExceptionInInitializerError at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:150) at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:102) at org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance(AbstractBeanFactory.java:1387) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:301) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:189) at com.learningspring.aop.TestAOP.main(TestAOP.java:19) Caused by: 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.springframework.aop.framework.Cglib2AopProxy.createEnhancer(Cglib2AopProxy.java:228) at org.springframework.aop.framework.Cglib2AopProxy.getProxy(Cglib2AopProxy.java:170) at org.springframework.aop.framework.ProxyFactoryBean.getProxy(ProxyFactoryBean.java:362) at org.springframework.aop.framework.ProxyFactoryBean.getSingletonInstance(ProxyFactoryBean.java:316) at org.springframework.aop.framework.ProxyFactoryBean.getObject(ProxyFactoryBean.java:242) at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:143) ... 5 more 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 @445b295b 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:199) at java.base/java.lang.reflect.Method.setAccessible(Method.java:193) 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) ... 19 more
这个错误的核心原因是Spring 3.0依赖的旧版本CGLIB(net.sf.cglib)与Java 9及以上版本的模块系统(JPMS)不兼容。
Java 9引入了模块访问控制机制,限制了对JDK内部API的反射访问。而旧版CGLIB需要通过反射调用ClassLoader.defineClass()方法来生成代理类,这个操作在Java 9+中被模块系统拦截,导致CGLIB初始化失败,最终引发ExceptionInInitializerError。
另外,Spring 3.0是2010年发布的版本,官方仅支持Java 5到Java 8,本身就没有适配Java 9+的模块系统。
提供三种可行的解决方式,按推荐优先级排序:
1. 降级Java版本到Java 8及以下
这是最直接的解决方案,因为Spring 3.0原本设计就兼容Java 8及更早版本。切换到Java 8运行代码,模块系统的限制不会生效,CGLIB可以正常生成代理类,代码无需修改。
2. 在Java 9+中添加JVM启动参数开放访问权限
如果必须使用Java 9+,可以通过添加JVM参数,允许未命名模块(CGLIB所在的模块)访问java.lang包的内部API:
--add-opens java.base/java.lang=ALL-UNNAMED
- 直接运行
java命令时:在启动命令末尾加上该参数,比如:java --add-opens java.base/java.lang=ALL-UNNAMED com.learningspring.aop.TestAOP - 使用Maven运行测试时:在
pom.xml的maven-surefire-plugin中配置参数:<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>3.0.0-M7</version> <configuration> <argLine>--add-opens java.base/java.lang=ALL-UNNAMED</argLine> </configuration> </plugin>
3. 升级Spring版本到支持Java 9+的版本
推荐长期解决方案是升级到Spring 5.0及以上版本(Spring 5最低支持Java 8,兼容Java 9+)。新版本Spring已经替换了旧的net.sf.cglib,改用适配JPMS的org.springframework.cglib(基于CGLIB 3.3+),同时也淘汰了过时的XmlBeanFactory。
修改后的TestAOP.java(Spring 5版本)
package com.learningspring.aop; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestAOP { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); aopA a = context.getBean("proxy", aopA.class); a.m(); } }
同时需要更新Spring依赖(以Maven为例),替换Spring 3.0的依赖为Spring 5.x版本:
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.29</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>5.3.29</version> </dependency> </dependencies>
内容的提问来源于stack exchange,提问作者Arun Pandey

