当前位置: 首页 > 面试题库 >

如何从动态代理显式调用默认方法?

师向文
2023-03-14
问题内容

由于Java
8接口可能具有默认方法。我知道如何从实现方法中显式调用该方法,即(请参阅在Java中显式调用默认方法)

但是,如何在代理服务器上使用反射来 显式 调用默认方法?

例:

interface ExampleMixin {

  String getText();

  default void printInfo(){
    System.out.println(getText());
  }
}

class Example {

  public static void main(String... args) throws Exception {

    Object target = new Object();

    Map<String, BiFunction<Object, Object[], Object>> behavior = new HashMap<>();

    ExampleMixin dynamic =
            (ExampleMixin) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),new Class[]{ExampleMixin.class}, (Object proxy, Method method, Object[] arguments) -> {

                //custom mixin behavior
                if(behavior.containsKey(method.getName())) {
                    return behavior.get(method.getName()).apply(target, arguments);
                //default mixin behavior
                } else if (method.isDefault()) {
                    //this block throws java.lang.IllegalAccessException: no private access for invokespecial
                    return MethodHandles.lookup()
                                        .in(method.getDeclaringClass())
                                        .unreflectSpecial(method, method.getDeclaringClass())
                                        .bindTo(target)
                                        .invokeWithArguments();
                //no mixin behavior
                } else if (ExampleMixin.class == method.getDeclaringClass()) {
                    throw new UnsupportedOperationException(method.getName() + " is not supported");
                //base class behavior
                } else{
                    return method.invoke(target, arguments);
                }
            });

    //define behavior for abstract method getText()
    behavior.put("getText", (o, a) -> o.toString() + " myText");

    System.out.println(dynamic.getClass());
    System.out.println(dynamic.toString());
    System.out.println(dynamic.getText());

    //print info should by default implementation
    dynamic.printInfo();
  }
}

编辑: 我知道在我如何反省地调用Java
8默认方法中
提出了类似的问题,但这由于两个原因未能解决我的问题:

  • 在提到的问题,旨在就如何通过反射调用它 一般 -所以默认和覆盖方法没有区别制成-这很简单,你只需要一个实例。
  • 答案之一-使用方法句柄-仅适用于讨厌的骇客(imho),例如将访问修饰符更改为lookup类的字段,这与“解决方案”的类别相同,如下所示:使用Java反射更改私有static final字段: 很高兴知道这是有可能的,但是我不会在生产中使用它 -我正在寻找一种“官方”的方式来做。

IllegalAccessException被抛出unreflectSpecial

Caused by: java.lang.IllegalAccessException: no private access for invokespecial: interface example.ExampleMixin, from example.ExampleMixin/package
at java.lang.invoke.MemberName.makeAccessException(MemberName.java:852)
at java.lang.invoke.MethodHandles$Lookup.checkSpecialCaller(MethodHandles.java:1568)
at java.lang.invoke.MethodHandles$Lookup.unreflectSpecial(MethodHandles.java:1227)
at example.Example.lambda$main$0(Example.java:30)
at example.Example$$Lambda$1/1342443276.invoke(Unknown Source)

问题答案:

如果您将具体的impl类用作lookupClass和invokeSpecial的调用者,则它应正确调用接口的默认实现(无需黑客进行私有访问):

Example target = new Example();
...

Class targetClass = target.getClass();
return MethodHandles.lookup()
                    .in(targetClass)
                    .unreflectSpecial(method, targetClass)
                    .bindTo(target)
                    .invokeWithArguments();

当然,这仅在您引用实现该接口的具体对象时才有效。

编辑:仅当所涉及的类(上述代码中的示例)可以从调用者代码(例如匿名内部类)进行私有访问时,此解决方案才有效。

MethodHandles /
Lookup类的当前实现不允许在无法从当前调用者类进行私有访问的任何类上调用invokeSpecial。有多种解决方法可用,但是所有这些都需要使用反射来使构造函数/方法可访问,如果安装了SecurityManager,则可能会失败。



 类似资料:
  • 问题内容: 拥有具有默认方法的接口的动态代理,如何调用默认方法?通过使用类似的方法,您可以得到名为的代理调用处理程序(这在某种程度上是正确的,因为您没有为此接口实现的类)。 我有一个使用ASM来创建实现接口的类并将此类调用委派给此类实例的解决方法。但这不是一个好的解决方案,特别是如果默认方法调用其他接口方法(您将获得委托人乒乓球)。JLS对此问题出人意料地保持沉默… 这里是一个小代码示例: 问题答

  • 问题内容: Java 8引入了默认方法,以提供扩展接口的功能,而无需修改现有的实现。 我想知道当由于不同接口中的默认实现冲突而导致该方法的默认实现被重写或不可用时,是否可以显式调用该方法的默认实现。 考虑上面的代码,你将如何A.foo()从B类的方法调用? 问题答案: 按照这篇文章,你在界面访问默认方法A使用 这可用于如下(假设接口A和C两个有默认的方法foo()) 并且可以同时具有方法,可以选择

  • 问题内容: 我想知道是否有可能实现这样的目标。 我有一个这样的游乐场: 我可以在中提供默认实现,但是如果需要默认实现中的所有内容以及其他内容,该怎么办? 它在某种程度上类似于es中的调用方法,可以满足实现每个属性等的要求。但是我看不到用实现相同的可能性。 问题答案: 我不知道您是否还在寻找答案,但是要做的方法是从协议定义中删除函数,将对象转换为对象,然后在其上调用方法: 由于某种原因,它仅在函数未

  • 我使用@enableCaching和@cacheable注释在SpringBoot应用程序中启用了缓存。缓存属性在application.yaml文件中定义。

  • 本文向大家介绍Java两种方式实现动态代理,包括了Java两种方式实现动态代理的使用技巧和注意事项,需要的朋友参考一下 一、JDK动态代理 Java 在 java.lang.reflect 包中有自己的代理支持,该类(Proxy.java)用于动态生成代理类,只需传入目标接口、目标接口的类加载器以及 InvocationHandler 便可为目标接口生成代理类及代理对象。我们称这个Java技术为:

  • 问题内容: 例如,我正在尝试做这样的事情 我收到一条错误消息,告诉我无法从静态环境中引用非静态变量。因此,如果这是真的,我将如何在main内部使用非静态方法? 问题答案: 你不能 非静态方法是必须在Test类的实例上调用的方法。创建Test的实例以在您的main方法中使用: