简而言之: 我有一些最后的课,我想为它创建动态代理。我该怎么做? 2.我可以将MethodHandle转换为Method吗?
详情 首先,是否存在将MethodHandle转换为Method的API?像java.lang.invoke.MethodHandles
中的东西public MethodHandle unreflect(Method m) throws IllegalAccessException;
但反过来了?
假设我想创建动态java.lang.reflect.Method。
是不确定的public final
class Method extends AccessibleObject implements GenericDeclaration,
Member ;
所以,如果我想使用JDK动态代理,我必须使用一些接口(例如Member)。虽然有2个主要的抽屉。首先,方法如
public Class<?>[] getParameterTypes();
等
public Class<?> getReturnType();
不是任何界面的一部分,而是广泛使用。
第二个缺点是它无法提供直接替换。也就是说,我无法将我的动态代理传递给期望java.lang.reflect.Method的代码。
另一种方法是使用CGLIB或Javaassist。 AFAIK,CGLIB无法代理最终班级,是吗? Javaassist可以代理最终类吗?如何从班级中“删除”最终标识符? AFAIL,Javvassist可以某种方式做到......
答案 0 :(得分:9)
这取决于您需要什么样的代理。基本上有三种方法可以实现这一点,其中两种在生产代码中是可行的。正如@probrekely所说,cglib或javassist的问题在于它们动态地创建了一个子类,这是最终类不可能的。你可以通过以下方式避免这种情况:
禁用字节码验证。 Java运行时验证字节代码,以确保不加载恶意字节代码。例如,当通过网络或互联网接收课程(例如小程序)时,这很重要。这样,您可以创建最终类的子类,因为字节代码验证程序不会阻止您。假设您可以在仅运行受信任的代码时禁用此验证。这可以通过运行:
来完成java -Xverify:none ApplicationName
然而,这是我向您推荐的解决方案。我不会将此aproach用于生产代码,但它肯定是最容易实现的解决方案。
在加载类之前或之后,从加载的类中删除final
修饰符。这可以通过使用Java agent来实现。可以在应用程序启动时通过命令行安装Java代理,也可以在运行时通过Attach API安装。使用像ASM这样的字节代码工具,您可以解析原始字节数组并从所有感兴趣的类中删除最终修饰符。也可以重新定义已加载的类。删除final
修饰符不会引入与旧类版本的冲突,因此始终可以进行重新定义。
执行与删除final
修饰符相同的描述,但重新定义加载的类以实际包含原始类中的所有检测逻辑。这个问题最需要付出最大努力,但这将使您的仪器转换为所有其他代码。这将是所有解决方案中最干净的解决方案。
答案 1 :(得分:0)
抱歉,您想要的不可能:
您可以使用CGLIB或Javassist为具体类创建代理,因为这些库动态生成您尝试代理的类的子类。 final
类不能被子类化,因此您无法以这种方式创建代理。
PowerMock确实允许您代理final
类和方法,但这是因为它在特殊的ClassLoader
下运行您的测试,它使用Javassist来修改您希望代理的类的字节码,因为它们'重装。 (你不希望在生产中使用这种东西,因为通常修改的“僵尸”版本的结果除了运行特定的模拟单元测试之外不会有什么好处。)
然而,PowerMock方法在这里不起作用 - 你想代理java.lang.reflect.Method
,它位于引导类路径上,因此会在任何PowerMock / Javassist类型工具之前加载,因此不能代理。