Java 字节伙伴的调试技术?

Java 字节伙伴的调试技术?,java,byte-buddy,Java,Byte Buddy,我正试图用Byte Buddy生成一个非常简单的代码 我有一个POJO类,其中一些字段用@SecureAttribute注释,对于这些字段,我希望覆盖getter实现并将调用重定向到SecurityService.getSecureValue()实现 原始类: public class Properties { @SecureAttribute protected String password; public String getPassword() {

我正试图用
Byte Buddy
生成一个非常简单的代码

我有一个POJO类,其中一些字段用
@SecureAttribute
注释,对于这些字段,我希望覆盖getter实现并将调用重定向到
SecurityService.getSecureValue()
实现

原始类:

public class Properties {
    @SecureAttribute
    protected String password;

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}
private Class<?> wrapProperties() throws IOException {

    DynamicType.Builder<?> byteBuddy = new ByteBuddy()
        .subclass(PropertiesImpl.class)
        .defineProperty("securityService", SecurityService.class);

    Arrays.stream(PropertiesImpl.class.getDeclaredFields())
        .filter(item -> item.getAnnotation(SecureAttribute.class) != null)
        .forEach(item -> byteBuddy
            .method(ElementMatchers.named(getGetterBeanName(item)))
            .intercept(new GetterWrapperImplementation(item)));

    DynamicType.Unloaded<?> unloadedType = byteBuddy.make();
    unloadedType.saveIn(new File("d:/temp/bytebuddy"));
    Class<?> wrapperClass = unloadedType.load(PropertiesImpl.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
        .getLoaded();

    return wrapperClass;
}

public static class GetterWrapperImplementation implements Implementation {
    public static final TypeDescription SS_TYPE;
    public static final MethodDescription SS_GET_SECURE_VALUE;

    private final Field filed;

    static {
        try {
            SS_TYPE = new TypeDescription.ForLoadedType(SecurityService.class);
            SS_GET_SECURE_VALUE = new MethodDescription.ForLoadedMethod(SecurityService.class.getDeclaredMethod("getSecureValue", String.class));
        }
        catch (final NoSuchMethodException | SecurityException e) {
            throw new RuntimeException(e);
        }
    }


    public GetterWrapperImplementation(Field filed) {
        this.filed = filed;
    }

    @Override
    public InstrumentedType prepare(final InstrumentedType instrumentedType) {
        return instrumentedType;
    }

    @Override
    public ByteCodeAppender appender(final Target implementationTarget) {
        final TypeDescription thisType = implementationTarget.getInstrumentedType();

        return new ByteCodeAppender.Simple(Arrays.asList(
            TypeCreation.of(SS_TYPE),

            // get securityService field
            MethodVariableAccess.loadThis(),
            FieldAccess.forField(thisType.getDeclaredFields()
                .filter(ElementMatchers.named("securityService"))
                .getOnly()
            ).read(),

            // get secure field
            MethodVariableAccess.loadThis(),
            FieldAccess.forField(thisType.getDeclaredFields()
                .filter(ElementMatchers.named(filed.getName()))
                .getOnly()
            ).read(),

            MethodInvocation.invoke(SS_GET_SECURE_VALUE),
            MethodReturn.of(TypeDescription.STRING)
        ));
    }
}
所需代理

public class PropertiesProxy {
    private SecurityService securityService;

    public void setSecurityService(SecurityService var1) {
        this.securityService = var1;
    }

    public SecurityService getSecurityService() {
        return this.securityService;
    }

    @Override
    public String getPassword() {
        return securityService.getSecureValue(password);
    }
}
发出一个字段很容易,但重写一个方法会变得复杂。我发现了一些与我的任务相关的样本,我试图应用这些样本,但似乎没有得到所需的结果

所以我的主要问题是:如何跟踪和调试代码生成器?我学到的第一件事是将类打印到文件:

    DynamicType.Unloaded<?> unloadedType = byteBuddy.make();
    unloadedType.saveIn(new File("d:/temp/bytebuddy"));
在这里,我并不完全理解如何查找错误。这是否意味着我使用了完全错误的方法实现,并且跳过了它?还是我对ElementMatchers错了?是否有一些跟踪或任何东西可以给我一个如何修复代码的线索

当前实施:

public class Properties {
    @SecureAttribute
    protected String password;

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}
private Class<?> wrapProperties() throws IOException {

    DynamicType.Builder<?> byteBuddy = new ByteBuddy()
        .subclass(PropertiesImpl.class)
        .defineProperty("securityService", SecurityService.class);

    Arrays.stream(PropertiesImpl.class.getDeclaredFields())
        .filter(item -> item.getAnnotation(SecureAttribute.class) != null)
        .forEach(item -> byteBuddy
            .method(ElementMatchers.named(getGetterBeanName(item)))
            .intercept(new GetterWrapperImplementation(item)));

    DynamicType.Unloaded<?> unloadedType = byteBuddy.make();
    unloadedType.saveIn(new File("d:/temp/bytebuddy"));
    Class<?> wrapperClass = unloadedType.load(PropertiesImpl.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
        .getLoaded();

    return wrapperClass;
}

public static class GetterWrapperImplementation implements Implementation {
    public static final TypeDescription SS_TYPE;
    public static final MethodDescription SS_GET_SECURE_VALUE;

    private final Field filed;

    static {
        try {
            SS_TYPE = new TypeDescription.ForLoadedType(SecurityService.class);
            SS_GET_SECURE_VALUE = new MethodDescription.ForLoadedMethod(SecurityService.class.getDeclaredMethod("getSecureValue", String.class));
        }
        catch (final NoSuchMethodException | SecurityException e) {
            throw new RuntimeException(e);
        }
    }


    public GetterWrapperImplementation(Field filed) {
        this.filed = filed;
    }

    @Override
    public InstrumentedType prepare(final InstrumentedType instrumentedType) {
        return instrumentedType;
    }

    @Override
    public ByteCodeAppender appender(final Target implementationTarget) {
        final TypeDescription thisType = implementationTarget.getInstrumentedType();

        return new ByteCodeAppender.Simple(Arrays.asList(
            TypeCreation.of(SS_TYPE),

            // get securityService field
            MethodVariableAccess.loadThis(),
            FieldAccess.forField(thisType.getDeclaredFields()
                .filter(ElementMatchers.named("securityService"))
                .getOnly()
            ).read(),

            // get secure field
            MethodVariableAccess.loadThis(),
            FieldAccess.forField(thisType.getDeclaredFields()
                .filter(ElementMatchers.named(filed.getName()))
                .getOnly()
            ).read(),

            MethodInvocation.invoke(SS_GET_SECURE_VALUE),
            MethodReturn.of(TypeDescription.STRING)
        ));
    }
}
private类wrapProperties()引发IOException{
DynamicType.Builder byteBuddy=新建byteBuddy()
.子类(PropertiesImpl.class)
.defineProperty(“securityService”,securityService.class);
Arrays.stream(PropertiesImpl.class.getDeclaredFields())
.filter(项目->项目.getAnnotation(SecureAttribute.class)!=null)
.forEach(项目->byteBuddy
.method(ElementMatchers.named(getGetterBeanName(项)))
.拦截(新的GetterRapperplementation(项目));
DynamicType.unloadedType=byteBuddy.make();
unloadedType.saveIn(新文件(“d:/temp/bytebuddy”);
Class wrapperClass=unloadedType.load(PropertiesImpl.Class.getClassLoader(),ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();
返回包装器类;
}
公共静态类GetterRapperImplementation实现{
公共静态最终类型描述SS_类型;
公共静态最终方法描述SS\U获取\U安全\U值;
私人最终现场存档;
静止的{
试一试{
SS_TYPE=新类型描述.ForLoadedType(SecurityService.class);
SS_GET_SECURE_VALUE=new MethodDescription.ForLoadedMethod(SecurityService.class.getDeclaredMethod(“getSecureValue”,String.class));
}
捕获(最终NoSuchMethodException | SecurityException e){
抛出新的运行时异常(e);
}
}
公共getterrapperperimentation(字段字段){
this.filed=归档;
}
@凌驾
公共仪表类型准备(最终仪表类型仪表类型){
返回仪表类型;
}
@凌驾
公共字节码追加器追加器(最终目标实现目标){
最终类型描述thisType=implementationTarget.getInstrumentedType();
返回新的ByteCodeAppender.Simple(Arrays.asList(
类型创建(SS_类型),
//获取安全服务字段
MethodVariableAccess.loadThis(),
FieldAccess.forField(thisType.getDeclaredFields()
.filter(ElementMatchers.named(“securityService”))
.getOnly()
).read(),
//获得安全域
MethodVariableAccess.loadThis(),
FieldAccess.forField(thisType.getDeclaredFields()
.filter(ElementMatchers.named(field.getName()))
.getOnly()
).read(),
调用(SS_GET_SECURE_VALUE),
MethodReturn.of(TypeDescription.STRING)
));
}
}
我所知道的事实是,
ByteCodeAppender appender(final Target implementation Target)
中的断点不会被命中,但同样不确定如何解释


谢谢。

DSL是不可变的。这意味着您必须始终拨打:

builder = builder.method(...).intercept(...);
由于这个原因,您的forEach没有达到您期望的效果


至于您的实现,您可以对一个字段使用MethodCall,并将另一个字段定义为参数。

Rafael,非常感谢您的帮助。在调试器下检查了一个小时Buddy的洞察之后,我突然意识到正在对
DynamicType.FieldDefinition
的实例调用method(),因此我很接近了。但总的问题是:如何调试ByteBuddy代码生成器?我的意思是,你不可能修复每个使用你的超级库的开发人员的错误。我会尝试在某个时候添加一些东西,但由于内部抽象级别和性能要求很高,这是不平凡的。你说得对,调试这个库应该会更容易。