我们有一些遗留的反映代理生成代码,如果你把它看成黑盒子,它基本上就是这样的:
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,大概是。
答案 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
不保证这是多么便携。