如何为最终类创建动态代理?

时间:2013-05-08 09:38:33

标签: java proxy mocking powermock cglib

简而言之: 我有一些最后的课,我想为它创建动态代理。我该怎么做? 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可以某种方式做到......

2 个答案:

答案 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类型工具之前加载,因此不能代理。