当我不知道它是否是一个lambda up-front时,我怎样才能安全地代理lambda?

时间:2014-10-30 23:14:24

标签: java reflection lambda java-8 cglib

我们有一些遗留的反映代理生成代码,如果你把它看成黑盒子,它基本上就是这样的:

Object someObject = new Anything();
Object debugObject = ProxyUtils.wrapWithDebugLogging(someObject);

wrapWithDebugLogging接受任何对象并覆盖它可以的任何方法(如果你扩展一个真正的类,最终方法显然是不可修复的),拦截它以记录有关该调用的消息,然后调用真实的方法

在内部,它使用cglib来完成工作,并在构造代理之前有一些保护逻辑,因为匿名类是最终的,但可以使用它们实现的超类或单个接口来处理:

Class<?> clazz = someObject.getClass();
Class<?> interfaces = clazz.getInterfaces();

// Anonymous classes are final so you can't extend them, but we know they only have one
// superclass or one interface.
if (clazz.isAnonymousClass()) {
    clazz = interfaces.length > 0 ?
            interfaces[0] : primaryType.getSuperclass();
}

Enhancer enhancer = new Enhancer();

if (clazz.isInterface()) {
    interfaces.add(clazz);
} else {
    enhancer.setSuperclass(primaryType);
}
enhancer.setInterfaces(interfaces.toArray(new Class[interfaces.size()]));

问题是Java 8的“lambda”类为isAnonymousClass()返回false。但我们希望将它们与匿名类完全相同。

已经pointed out before无法确定某个类是“按设计”的lambda类。但这似乎更像是反射API中缺少的东西,而且肯定不是Java第一次“忘记”在新API上添加一些明显的东西。

因此,如果没有API中的此功能,是否有一种明智的方法来区分lambda和非lambda?我可以看到isSynthetic()返回true,但是对于各种其他事情,它也会返回true,大概是。

3 个答案:

答案 0 :(得分:2)

根据是否为lambda生成类的问题,您不应创建条件代码。毕竟,只有该类的属性才重要。

该类为final,因此您不能将其子类化。即使它不是final,由于它只有private个构造函数,因此无法实现子类。它实现了interface。这些是班级的相关属性。

在没有任何lambda表达式的情况下遇到相同的场景并不是不现实的:

final class NotALambda implements Function<String,String> {

    public static final Function<String,String> INSTANCE=new NotALambda();

    private NotALambda() {}

    public String apply(String t) {
        return t.toLowerCase();
    }
}

为什么要将此类视为与通过
生成的类不同 Function<String,String> f=String::toLowerCase;?它具有相同的属性和创建代理的相同障碍。在评论中,您说您希望根据方法是否被声明为final的问题而有所作为。这样做的意义更小,因为我可以在上面的例子中为方法添加final修饰符,而不会改变任何内容,无论是语义还是创建代理时都会遇到的困难。

答案 1 :(得分:1)

我不认为这个限制(如果你想称之为)应该是一个问题。

如果您正确编程到适当的接口,则使您的代理具有超类(除了Object之外)变得不必要。在可用时设置代理的超类(没有private构造函数,而不是final等)。

在所有情况下,您需要做的就是捕获代理对象,目标,拦截其上的所有方法调用,执行日志记录,然后将调用委托或路由到它。

public static Object wrapWithDebugLogging(Object target) {
    ... // prepare the enhancer as described above
    enhancer.setCallback(new MethodInterceptor() {        
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            logger.debug("Some useful logging message.");
            return method.invoke(target, args);            
        }
    });
    return enhancer.create();
}

如果调用的方法是final,你没有试图覆盖它,你只是在拦截它。

答案 2 :(得分:0)

以下是快速实验的结果。 foo方法只输出其参数的getClass().toString()。 (getClass().getName()输出相同的结果。)

    foo (new Runnable() {
            @Override
            public void run() {
                System.out.println("run");
            }
         });

输出:Test32$1

    foo (() -> System.out.println("run"));

输出:Test32$$Lambda$1/640070680

不保证这是多么便携。