我正在尝试使用ASM Framework动态创建子类。 我能够创建类并实例化它。但是当我尝试做的时候
org.apache.commons.beanutils.BeanUtils.copyProperties(processedEntity, entity);
它引发了这个异常:
java.lang.NoClassDefFoundError: com/wheelsup/app/benefits/service/XpOWErhNBiBeanInfo (wrong name: com/wheelsup/app/benefits/service/XpOWErhNBi)
这是我用来创建子类的代码:
Class<? extends T> get() throws Exception {
String superClassInternalName = getInternalName(superClass);
String subClassSimpleName = RandomStringUtils.random(10, true, false);
String subClassInternalName = getClass().getPackage().getName().replaceAll("\\.", "/").concat("/").concat(subClassSimpleName);
String subClassName = getClass().getPackage().getName().concat(".").concat(subClassSimpleName);
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
classWriter.visit(Opcodes.V1_6,
ACC_PUBLIC,
subClassInternalName,
null,
superClassInternalName,
null);
visitDefaultConstructor(classWriter, superClassInternalName);
classWriter.visitEnd();
return SubClassLoader.<T>init().load(classWriter.toByteArray(), subClassName);
}
private void visitDefaultConstructor(ClassWriter classWriter, String superClassInternalName) {
MethodVisitor methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
methodVisitor.visitCode();
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitMethodInsn(INVOKESPECIAL, superClassInternalName, "<init>", "()V");
methodVisitor.visitInsn(RETURN);
methodVisitor.visitMaxs(0, 0);
methodVisitor.visitEnd();
}
private static class SubClassLoader<T> {
private final ClassLoader contextClassLoader;
private SubClassLoader(ClassLoader contextClassLoader) {
this.contextClassLoader = contextClassLoader;
}
static <U> SubClassLoader<U> init() {
return new SubClassLoader<>(Thread.currentThread().getContextClassLoader());
}
@SuppressWarnings("unchecked")
Class<? extends T> load(byte[] classBytes, String className) throws Exception {
return (Class<? extends T>) new SubClassLoader.DynamicClassLoader(contextClassLoader, classBytes).loadClass(className);
}
private static class DynamicClassLoader extends ClassLoader {
private byte[] rawClassBytes;
private DynamicClassLoader(ClassLoader contextClassLoader, byte[] classBytes) {
super(contextClassLoader);
this.rawClassBytes = classBytes;
}
@Override
public Class findClass(String name) {
return defineClass(name, this.rawClassBytes, 0, this.rawClassBytes.length);
}
}
}
我不明白 BeanInfo 的事情;它是什么?以及如何解决我的问题?
感谢。
答案 0 :(得分:1)
所以问题出在ClassLoader
中private static class SubClassLoader<T> {
private final ClassLoader contextClassLoader;
private SubClassLoader(ClassLoader contextClassLoader) {
this.contextClassLoader = contextClassLoader;
}
static <U> SubClassLoader<U> init() {
return new SubClassLoader<>(Thread.currentThread().getContextClassLoader());
}
@SuppressWarnings("unchecked")
Class<? extends T> load(byte[] classBytes, String className) throws Exception {
return (Class<? extends T>) new DynamicClassLoader(contextClassLoader, classBytes, className).loadClass(className);
}
private static class DynamicClassLoader extends ClassLoader {
private byte[] classBytes;
private final String className;
private DynamicClassLoader(ClassLoader contextClassLoader, byte[] classBytes, String className) {
super(contextClassLoader);
this.classBytes = classBytes;
this.className = className;
}
@Override
public Class findClass(String className) throws ClassNotFoundException {
if (StringUtils.equals(this.className, className)) {
return defineClass(className, this.classBytes, 0, this.classBytes.length);
}
throw new ClassNotFoundException(className);
}
}
}
答案 1 :(得分:1)
问题在于,无论调用者要求哪个类,您的findClass
实现都会尝试返回一个生成的类。在某些情况下,无法加载类是正确的操作。
BeanUtils
类依赖于Introspector
,它允许为被检查类提供可选的显式beaninfo实现,因此如果被要求BeanInfo
Foo
,它将会尝试首先加载一个类FooBeanInfo
,如果失败,它将为Foo
构建一个通用的bean信息。
但是,由于您的findClass
实施尝试(重新)以错误的名称XpOWErhNBi
构建XpOWErhNBiBeanInfo
类,而不是报告缺少XpOWErhNBiBeanInfo
,因此出现问题。
您必须更改SubClassLoader
以接收生成的类的预期名称。然后,您可以将findClass
实施更改为
@Override
public Class findClass(String name) throws ClassNotFoundException {
if(!name.equals(expectedName))
throw new ClassNotFoundException(name);
return defineClass(name, this.rawClassBytes, 0, this.rawClassBytes.length);
}
一个更简单但是hacky的解决方案是在第一个类构造之后null
rawClassBytes
并为每个后续类加载请求抛出ClassNotFoundException
作为继承标准{{ 1}}实现保证仅为尚未加载的类调用loadClass
,因此只要立即加载生成的类的程序逻辑没有改变,所有后续请求都是关于不同的,不支持的类。
然而,由于关键点是程序逻辑不能改变,我不建议使用hacky,脆弱的解决方案。将生成的类的名称传递给自定义加载器并验证它,首先是更多的代码,但更清晰。