Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/360.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在Java中实现包装器装饰器?_Java_Decorator_Bytecode_Byte Buddy - Fatal编程技术网

如何在Java中实现包装器装饰器?

如何在Java中实现包装器装饰器?,java,decorator,bytecode,byte-buddy,Java,Decorator,Bytecode,Byte Buddy,问题在于创建现有对象的动态增强版本 我无法修改对象的类。相反,我必须: 子类化 将现有对象包装到新的类中 将所有原始方法调用委托给包装对象 实现由另一个接口定义的所有方法 要添加到现有对象的接口是: public interface EnhancedNode { Node getNode(); void setNode(Node node); Set getRules(); void setRules(Set rules); Map getGroups(); v

问题在于创建现有对象的动态增强版本

我无法修改对象的
。相反,我必须:

  • 子类化
  • 将现有对象包装到新的
    类中
  • 将所有原始方法调用委托给包装对象
  • 实现由另一个接口定义的所有方法
要添加到现有对象的接口是:

public interface EnhancedNode {

  Node getNode();
  void setNode(Node node);

  Set getRules();
  void setRules(Set rules);

  Map getGroups();
  void setGroups(Map groups);

}
使用,我成功地创建了子类并实现了我的接口。问题是对包装对象的委托。我发现实现这一点的唯一方法是使用反射,因为反射速度太慢(我的应用程序负载很重,性能很关键)

到目前为止,我的代码是:

Class<? extends Node> proxyType = new ByteBuddy()
     .subclass(node.getClass(), ConstructorStrategy.Default.IMITATE_SUPER_TYPE_PUBLIC)
     .method(anyOf(finalNode.getClass().getMethods())).intercept(MethodDelegation.to(NodeInterceptor.class))
     .defineField("node", Node.class, Visibility.PRIVATE)
     .implement(EnhancedNode.class).intercept(FieldAccessor.ofBeanProperty())
     .defineField("groups", Map.class, Visibility.PRIVATE)
     .implement(EnhancedNode.class).intercept(FieldAccessor.ofBeanProperty())
     .defineField("rules", Set.class, Visibility.PRIVATE)
     .implement(EnhancedNode.class).intercept(FieldAccessor.ofBeanProperty())
     .make()
     .load(getClass().getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
     .getLoaded();
enhancedClass = (Class<N>) proxyType;
EnhancedNode enhancedNode = (EnhancedNode) enhancedClass.newInstance();
enhancedNode.setNode(node);

一切正常,但拦截方法太慢,我计划直接使用ASM来添加每个节点方法的实现,但我希望有一种更简单的方法使用Byte Buddy。

您可能希望使用
管道而不是反射API:

public class NodeInterceptor {

  @RuntimeType
  public static Object intercept(@Pipe Function<Node, Object> pipe,
                                 @FieldValue("node") Node proxy) throws Exception {
      return proxy != null
        ? pipe.apply(proxy);
        : null;
  }
}
你自己。类型和方法的名称是不相关的。安装类型为:

MethodDelegation.to(NodeInterceptor.class)
                .appendParameterBinder(Pipe.Binder.install(Function.class));
但是,您确定反射部分是应用程序性能问题的关键点吗?您是否正确缓存生成的类,并且缓存是否有效工作?反射API比它的声誉更快,特别是因为使用字节伙伴往往意味着单态调用站点

最后,一些一般性的反馈。你在打电话吗

.implement(EnhancedNode.class).intercept(FieldAccessor.ofBeanProperty())

多次。这没有效果。另外,
method.getDeclaringClass().cast(节点)
也不是必需的。反射API为您进行转换。

我不知道,但我看到一个新的代理类有可能被一次又一次地创建。是否存在某种缓存?顺便问一下,您如何强制给定节点的类具有公共默认构造函数?您有一个接口,您添加了字段和方法。。。为什么你不能在没有这个动态增强的情况下自己创建一个包装类呢?@HannoBinder这个类是为每个节点子类缓存的,你是对的,这些类需要默认的构造函数,这是另一个开放的问题;)@AdamSkywalker我需要动态增强器,因为节点在巨大的集合中,如果我将它们包装在不同的类中,每次客户端需要节点子集时,我都必须在集合中循环提取它们,这对性能是一个问题。使用一个子类,我可以返回一组包装器,而无需进行转换。作为性能说明,在我的单元测试中,我调用了1.000.000.000个方法(包括添加的和前置的方法),通过反射,我有将近10秒的执行时间,使用您的解决方案,我有大约9毫秒的执行时间,做得好!太好了,谢谢你的反馈。我想知道您运行的是什么Java版本。反射比最新版本改进了很多,我在最新的Java 8虚拟机上运行了所有性能测试。单元测试在Mac OS X上使用Oracle JDK 1.8.045运行,反射API的性能仅与这样的关键组件相关,当调用太多时,反射API的性能会因调用站点单态而发生很大变化。这与cglib类似。Byte Buddy的目标是呼叫站点单态性,我想这就是你正在经历的。(作为对未来读者的说明。)关于性能的另一个说明,使用OracleJDK1.7,管道解决方案变成了大约3秒,而不是JDK1.8的9毫秒
MethodDelegation.to(NodeInterceptor.class)
                .appendParameterBinder(Pipe.Binder.install(Function.class));
.implement(EnhancedNode.class).intercept(FieldAccessor.ofBeanProperty())