You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用ByteBuddy动态修改类报错:Cannot inject already loaded type

问题:ByteBuddy二次调用apply后抛出Cannot inject already loaded type异常

Exception in thread "main" java.lang.IllegalStateException: Cannot inject already loaded type: class pl.edu.testowy.entity.Author

我想用ByteBuddy给Author类添加多个变量,但第二次调用apply方法时抛出上述异常。我知道这和ClassLoader有关,但修改ClassLoader后没解决问题,相关代码如下:

相关代码

Reflection类

package pl.edu.wat.testowy.reflection;

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.ClassFileLocator;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.implementation.MethodCall;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.pool.TypePool;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import static net.bytebuddy.matcher.ElementMatchers.named;

public class Reflection {
    private TypeDescription entityDefinition;
    private TypeDescription requestDefinition;
    private TypeDescription responseDefinition;

    private TypeDescription mapperDefinition;
    private TypePool typePool;
    private ByteBuddy byteBuddy;

    public Reflection() {
        this.typePool = TypePool.Default.ofSystemLoader();
        this.byteBuddy = new ByteBuddy();
        this.entityDefinition = typePool.describe("pl.edu.wat.testowy.entity.Author").resolve();
        this.requestDefinition = typePool.describe("pl.edu.wat.testowy.dto.AuthorRequest").resolve();
        this.responseDefinition = typePool.describe("pl.edu.wat.testowy.dto.AuthorResponse").resolve();
        this.mapperDefinition = typePool.describe("pl.edu.wat.testowy.mapper.AuthorMapper").resolve();

    }


    FieldInformation fieldInformation = new FieldInformation();
    FieldInformation2 fieldInformation2 = new FieldInformation2();


    //nie beda to pola final bo one beda sie zmienialy
    public static void apply(String test){

        var ref = new Reflection();
        ref.applyEntity(test);
        ref.applyRequest(test);
        ref.applyResponse(test);
        ref.applyAuthorMapper(test);

    }

    public List<String> getFieldNames() {
        FieldInformation2.readJson("fields2.json");
        List<FieldInformation2> fields = FieldInformation2.getFields();
        List<String> fieldNames = new ArrayList<>();
        fields.forEach(f -> fieldNames.add(f.getFieldName()));
        return fieldNames;
    }

    private void applyAuthorMapper(String test) {//musimy wyciagnac mappera

        DynamicType.Builder <Object> builder = byteBuddy
                .redefine(mapperDefinition,
                        ClassFileLocator.ForClassLoader.ofSystemLoader())
                .method(named("fillAuthorRequest"))
                .intercept(MethodCall.invoke(setterAuthorEntity(test))
                        .onArgument(0)
                        .withMethodCall(MethodCall
                                .invoke(getterRequest(test))
                                .onArgument(1)))
                .method(named("fillAuthor"))
                .intercept(MethodCall.invoke(setterAuthorResponse(test))
                        .onArgument(0)
                        .withMethodCall(MethodCall
                                .invoke(getterEntity(test))
                                .onArgument(1)));

        try(var unloadedAuthor = builder.make()){
            mapperDefinition = unloadedAuthor.load(ClassLoader.getSystemClassLoader(), ClassLoadingStrategy.Default.INJECTION)
                    .getTypeDescription();

        } catch(IOException e){
            throw new RuntimeException();
        }

    }

    private MethodDescription getterEntity(String test){
        return  entityDefinition
                .getDeclaredMethods()
                .filter(ElementMatchers.isGetter(test))
                .stream()
                .findFirst()
                .orElseThrow();
    }
    private MethodDescription setterAuthorResponse(String test) {
        return  responseDefinition
                .getDeclaredMethods()
                .filter(ElementMatchers.isSetter(test))
                .stream()
                .findFirst()
                .orElseThrow();
    }

    private MethodDescription getterRequest(String test) {
        return  requestDefinition
                .getDeclaredMethods()
                .filter(ElementMatchers.isGetter(test))
                .stream()
                .findFirst()
                .orElseThrow();
    }

    private MethodDescription setterAuthorEntity(String test) {
        return  entityDefinition
                .getDeclaredMethods()
                .filter(ElementMatchers.isSetter(test))
                .stream()
                .findFirst()
                .orElseThrow();
    }

    private void applyResponse(String test) {

        DynamicType.Builder <Object> builder = byteBuddy
                .redefine(responseDefinition,
                        ClassFileLocator.ForClassLoader.ofSystemLoader())
                .defineProperty(test,typePool.describe("java.lang.String").resolve());

        try(var unloadedAuthor = builder.make()){
            responseDefinition = unloadedAuthor.load(ClassLoader.getSystemClassLoader(), ClassLoadingStrategy.Default.INJECTION)
                    .getTypeDescription();


        } catch(IOException e){
            throw new RuntimeException();
        }

    }

    private void applyRequest(String test) {
        DynamicType.Builder <Object> builder = byteBuddy
                .redefine(requestDefinition,
                        ClassFileLocator.ForClassLoader.ofSystemLoader())
                .defineProperty(test,typePool.describe("java.lang.String").resolve());

        try(var unloadedAuthor = builder.make()){
            requestDefinition = unloadedAuthor.load(ClassLoader.getSystemClassLoader(), ClassLoadingStrategy.Default.INJECTION)
                    .getTypeDescription();


        } catch(IOException e){
            throw new RuntimeException();
        }

    }

    public void applyEntity(String test) {
        DynamicType.Builder <Object> builder = byteBuddy
                .redefine(entityDefinition,
                        ClassFileLocator.ForClassLoader.ofSystemLoader())
                .defineProperty(test,typePool.describe("java.lang.String").resolve());

        try(var unloadedAuthor = builder.make()){
            entityDefinition = unloadedAuthor.load(ClassLoader.getSystemClassLoader(), ClassLoadingStrategy.Default.INJECTION)
                    .getTypeDescription();

        } catch(IOException e){
            throw new RuntimeException();
        }

    }


}

TestowyApplication类

package pl.edu.wat.testowy;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import pl.edu.wat.testowy.reflection.Reflection;


@SpringBootApplication
public class TestowyApplication {

    public static void main(String[] args) {
        //tak jak tu korzystam z mechanizmu refleksji tak wystarczy utworzyc zalozmy 10 obiektow, gdzie podajemy nazwe i typ zmiennej
        Reflection.apply("test");
        Reflection.apply("test2");
        SpringApplication.run(TestowyApplication.class, args);
    }

}

解决方案

核心原因

每次调用Reflection.apply()都会新建Reflection实例,且每次都用ClassLoadingStrategy.Default.INJECTION尝试注入已被JVM加载的类。JVM不允许修改已加载类的结构,INJECTION策略仅适用于加载未被加载过的类。

修复方案1:一次性处理所有字段

将需要添加的字段收集后一次性完成修改,避免多次触发类加载:

// 修改Reflection的apply方法,接收字段列表
public static void apply(List<String> fields) {
    var ref = new Reflection();
    fields.forEach(field -> {
        ref.applyEntity(field);
        ref.applyRequest(field);
        ref.applyResponse(field);
        ref.applyAuthorMapper(field);
    });
}

// 修改main方法,一次性传入所有字段
public static void main(String[] args) {
    Reflection.apply(List.of("test", "test2"));
    SpringApplication.run(TestowyApplication.class, args);
}

修复方案2:使用自定义类加载器

如果必须分多次修改,可使用自定义类加载器加载修改后的类(不同类加载器加载的同名类在JVM中视为不同类型):

// 在加载类时替换为自定义类加载器和WRAPPER策略
ClassLoader customClassLoader = new URLClassLoader(new URL[0], ClassLoader.getSystemClassLoader());
mapperDefinition = unloadedAuthor.load(customClassLoader, ClassLoadingStrategy.Default.WRAPPER)
        .getTypeDescription();

注意:后续代码需统一使用该自定义类加载器访问修改后的类,否则会出现类型不匹配问题。

额外优化点

将Reflection中的entityDefinition等字段改为静态,或在实例间传递修改后的TypeDescription,确保每次修改都是基于上一次修改后的类结构,而非重新读取原始类信息。

内容的提问来源于stack exchange,提问作者Adamero

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 04:15:27