Marco‘s Java【ByteBudy字节码与Spring AOP共用造成ClassNotFound】

文章描述了一个在使用ByteBuddy进行类代理时与SpringAOP集成出现的ClassNotFound异常问题。原因是ByteBuddy生成的代理类在Spring的bean初始化过程中未能被正确加载。解决方案是通过ByteBuddy的INJECTION方式将代理类注入到AppClassLoader中,从而解决加载问题。
摘要由CSDN通过智能技术生成

Marco‘s Java【ByteBudy字节码与Spring AOP共用造成ClassNotFound】

背景

为了避免与Spring AOP技术强绑定,提升组件的可移植性(如将sdk方式转agent方式),提高代理的执行效率等原因,

在Repeat组件中采用ByteBuddy技术对指定类进行代理,但是使用@PreventRepeat组件时发生下述异常

Initialization of bean failed; nested exception is org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class com.corpgovernment.organization.controller.TestController$ByteBuddy$pX8WFoCZ: Common causes of this problem include using a final class or a non-visible class; nested exception is org.springframework.cglib.core.CodeGenerationException: java.lang.NoClassDefFoundError-->com/corpgovernment/organization/controller/TestController$ByteBuddy$pX8WFoCZ] with root cause
java.lang.ClassNotFoundException: com.corpgovernment.organization.controller.TestController$ByteBuddy$pX8WFoCZ
	at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
	...
 	at org.springframework.cglib.proxy.Enhancer.createHelper(Enhancer.java:572)
	at org.springframework.cglib.proxy.Enhancer.createClass(Enhancer.java:419)
	at org.springframework.aop.framework.ObjenesisCglibAopProxy.createProxyClassAndInstance(ObjenesisCglibAopProxy.java:57)
	at org.springframework.aop.framework.CglibAopProxy.getProxy(CglibAopProxy.java:206)
	at org.springframework.aop.framework.ProxyFactory.getProxy(ProxyFactory.java:110)
	at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.createProxy(AbstractAutoProxyCreator.java:482)
	at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.wrapIfNecessary(AbstractAutoProxyCreator.java:351)
	at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.postProcessAfterInitialization(AbstractAutoProxyCreator.java:300)

根据上述异常可以找到一些细节点

1、Class找不到

2、在执行AbstractAutoProxyCreator时找不到

3、底层执行 URLClassLoader 时找不到

这里有两个问题

1、为什么在执行TestController时还要创建Proxy?

2、为什么URLClassLoader找不到Class?

查找问题

为什么在执行TestController时还要创建Proxy?

针对第一个问题,我们可以通过日志推断发现

找不到的Class名称为com.corpgovernment.organization.controller.TestController$ByteBuddy$pX8WFoCZ

我们重新启动项目对AbstractAutoProxyCreator的postProcessAfterInitialization方法进行Debug,发现启动加载的Class是com.corpgovernment.organization.controller.TestController

但由于我们采用ByteBuddy对类com.corpgovernment.organization.controller.TestController进行了代理操作,代理后生成的Class文件名称为com.corpgovernment.organization.controller.TestController$ByteBuddy$pX8WFoCZ

而我们执行生成代理操作的时机是InitializingBean,时机晚于postProcessAfterInitialization,所以com.corpgovernment.organization.controller.TestController$ByteBuddy$pX8WFoCZ没有被Proxy代理

public class ReplaceBeanDefinitionRegistryPostProcessor implements InitializingBean {

@Override
public void afterPropertiesSet() throws Exception {
    ByteCodeDynamicEnhancer.enhance("repeat");
    ...
}

}

这也就解释了为什么后续在执行com.corpgovernment.organization.controller.TestController B y t e B u d d y ByteBuddy ByteBuddypX8WFoCZ类时会进行postProcessAfterInitialization方法创建代理

为什么URLClassLoader找不到Class?

在解释这个问题之前,我们先了解下URLClassLoader是什么?

继承关系:

java.lang.Object

       --- java.lang.ClassLoader
              --- java.security.SecureClassLoader
                      ---  java.net.URLClassLoader
                            --- sun.misc.Launcher$ExtClassLoader

 
java.lang.Object

       --- java.lang.ClassLoader
              --- java.security.SecureClassLoader
                      ---  java.net.URLClassLoader
                            --- sun.misc.Launcher$AppClassLoader

根据上述关系可以看出URLClassLoader是sun.misc.Launcher$ExtClassLoadersun.misc.Launcher$AppClassLoader的父类,以下是双亲委派的类加载机制

掌握了上述知识点后,我们对AbstractAutoProxyCreator的createProxy方法进行Debug,查看当前的ClassLoader

可见当前ClassLoader为AppClassLoader

由此推断第二个问题产生的原因是AppClassLoader中加载不到com.corpgovernment.organization.controller.TestController$ByteBuddy$pX8WFoCZ导致

解决方案

由于篇幅问题,不对ByteBuddy做太多介绍,以下Debug内容为ByteBuddy对com.corpgovernment.organization.controller.TestController类进行代理并生成新的类的代码片段

可以看出被代理生成后的com.corpgovernment.organization.controller.TestController的ClassLoader是ByteArrayClassLoader

根据这个线索,我们可以得出两个解决方案

  • 执行AOP时,指定ClassLoader为 ByteArrayClassLoader

  • ByteBuddy进行类加载时,将类注入到 AppClassLoader

考虑到执行AOP时指定ClassLoader可能对其他自定义ClassLoader定制类的操作会产生影响,且Spring无法针对具体的类进行指定ClassLoader操作,所以我们采用第二种方式。

在ByteBuddy注入时可以采用INJECTION的方式将类注入到指定的ClassLoader中,这里我们选择将类注入到AppClassLoader中,问题完美解决!

INJECTION方式本质上是采用反射的方式进行类注入的,而类注入需要访问JVM内部方法,这些方法由安全管理器和Java平台模块系统封闭。

从Java 11开始,除非明确打开这些包,否则无法访问这些方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值