我正在努力创建一个" custom"具有字节伙伴的字段的setter方法。 Buddy自己的机制允许非常容易地实现标准的setter / getter方法,但是,我正在寻找一种优雅的方法来扩展setter并增加一些逻辑。
为了简化示例,我们假设我们有一个类A,它有一个方法setChanged(String)。 目标是制作A的子类,添加具有相应访问方法的字段。 问题是,我想从每个添加的setter方法中调用setChanged(" fieldName")。
public void setName(String name)
{
setChanged("name");
this.name = name;
}
对于"正常" setter方法,byte byddy实现将是:
new ByteBuddy()
.subclass(A.class)
.name("B")
.defineField("name", Integer.TYPE, Visibility.PUBLIC)
// args is a ArrayList<Class<?>>
.defineMethod(getSetterName(name), Void.TYPE, args, Visibility.PUBLIC)
.intercept( FieldAccessor.ofField(name) )
Bytecode我看起来像这样:
L0
ALOAD 0 // Loads the this reference onto the operand stack
ILOAD 1 // Loads the integer value of the local variable 1 (first method arg)
PUTFIELD package/B.name : I // stores the value to the field
L1
ALOAD 0
LDC "name"
INVOKEVIRTUAL package/A.setChanged (Ljava/lang/String;)V
RETURN
我的问题是:在这种情况下有没有办法重新使用FieldAccessor?
答案 0 :(得分:3)
截至今天,您需要定义自定义Instrumentation
来执行此类自定义作业。正如评论中指出的那样,您可以使用Instrumentation.Compound
将新行为添加到例如FieldAccessor.ofBeanProperty()
之前,从而重用字段访问器代码。
为了添加自定义代码,Byte Buddy知道不同的抽象级别:
Instrumentation
:定义方法的实现方式。检测能够定义实现方法所需的其他字段,方法或静态初始化块。此外,它确定是否要为类型定义方法。ByteCodeAppender
发出Instrumentation
,确定定义的方法是否为抽象方法,如果实现方法,则确定方法的字节代码。StackManipulation
是一个字节代码指令,对操作数堆栈的大小有一定的影响。堆栈操作是为实现非抽象方法而组建的。为了在字节代码中调用方法,您需要将所有参数(包括this
)加载到堆栈中,并在放置所有这些参数后调用该方法。这可以按如下方式完成:
this
MethodVariableAccess.REFERENCE.loadFromIndex(0)
引用加载到堆栈中
ByteCodeAppender
。使用TextConstant
,可以将名称放在堆栈上。MethodInvocation
调用方法,其中setChanged
方法可以从创建的检测类型的TypeDescription
中提取以Instrumentation
作为论据。当然,这不是很漂亮,而且Byte Buddy希望从用户隐藏这个字节代码级API并在DSL或普通Java中表达任何内容。因此,您可能会很高兴听到我正在使用Byte Buddy版本0.4,它附带了一些您可以使用的功能。对于您的示例,您可以使用Byte Buddy的瑞士军刀MethodDelegation
的扩展形式来实现自定义setter。方法委派允许您通过使用注释委托对任何Java方法的调用来实现方法。
假设您的bean实现了一个类型:
interface Changeable {
void setChanged(String field);
}
您可以使用以下方法拦截方法调用:
class Interceptor {
static void intercept(@This Changeable thiz, @Origin Method method) {
thiz.setChanged(method.getName());
}
}
使用方法委托,Byte Buddy将在调用方法时始终调用拦截器。拦截器方法是传递的参数,用于描述特定拦截的上下文。在上面,传递this
引用和截获的方法。
当然,我们仍然缺少该领域的实际设置。但是,使用Byte Buddy 0.4,您现在可以创建一个新的Instrumentation
,如下所示:
MethodDelegation.to(Interceptor.class).andThen(FieldAccessor.ofBeanProperty())
使用此委托,Byte Buddy首先调用拦截器(然后删除任何潜在的返回值),最后将作为参数传递的Instrumentation
应用于andThen
方法。