【发布时间】:2018-05-05 01:15:38
【问题描述】:
假设我想在运行时创建另一个类可以使用的自定义类。
package redefineconcept;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import java.lang.reflect.InvocationTargetException;
public class LoadingTest {
public static final String HelloWorldTag = "$HelloWorld";
public static void main(String[] args){
new LoadingTest().run();
}
private void run(){
InstanceUser u = new InstanceUser();
u.start();
Class <?> createdClass = createAndLoadFor(InstanceUser.class);
System.out.println(String.format("created new class %s", createdClass.getName()));
InstanceUser.canAccess = true;
try {
u.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private Class<?> createAndLoadFor(Class<?> clazz){
ByteBuddy byteBuddy = new ByteBuddy();
String newClassName = clazz.getName() + LoadingTest.HelloWorldTag;
DynamicType.Builder builder = byteBuddy
.subclass(Object.class)
.name(newClassName)
;
DynamicType.Unloaded<?> newType = builder.make();
return newType
.load(clazz.getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
.getLoaded();
}
}
class InstanceUser extends Thread{
public static volatile boolean canAccess = false;
Object instance;
@Override
public void run() {
while(!canAccess){}
String cn = this.getClass().getName() + LoadingTest.HelloWorldTag;
Class clazz;
try{
clazz = Class.forName(cn);
}catch(ClassNotFoundException e){
e.printStackTrace();
throw new RuntimeException();
}
try{
instance = clazz.getConstructor().newInstance();
}catch(NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e){
e.printStackTrace();
throw new RuntimeException();
}
}
}
这行得通。
不过,the ByteBuddy tutorial 建议
您可能会认为遇到循环依赖的机会无关紧要,因为您一次创建一种动态类型。但是,类型的动态创建可能会触发所谓的辅助类型的创建。
这些类型由 Byte Buddy 自动创建,以提供对您正在创建的动态类型的访问。
因此,我们建议您尽可能通过创建特定的 ClassLoader 来加载动态创建的类,而不是将它们注入到现有的类中。
我对类加载器(或 ByteBuddy,就此而言)知之甚少,但本教程似乎表明类加载器是按层次排序的。
如果是这样,应该可以将新的类加载器链接到clazz.getClassLoader(),对吧?
好吧,ClassLoadingStrategy.Default.WRAPPER 和 ClassLoadingStrategy.Default.CHILD_FIRST 都没有。
两个结果
created new class redefineconcept.InstanceUser$HelloWorld
java.lang.ClassNotFoundException: redefineconcept.InstanceUser$HelloWorld
这让我相信
通常,Java 类加载器会在尝试直接加载给定名称的类型之前查询其父类加载器。
意味着他们只查询父 ClassLoaders 而不是子。
是这样吗?
这里有没有可能避免使用ClassLoadingStrategy.Default.INJECTION?
【问题讨论】:
-
既然你用的是
Class.forName(cn),为什么还要坚持改变类加载器的解析策略呢?使用自定义类加载器,传递它并将Class.forName(cn)替换为customClassLoader.loadClass(cn)...
标签: java classloader code-generation instrumentation byte-buddy