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

ale*_*ail 9 java proxy mocking powermock cglib

简而言之: 1.我有一些最后的课,我想为它创建动态代理.我该怎么做?2.我可以将MethodHandle转换为Method吗?

详细信息 首先,是否存在将MethodHandle转换为Method的API?像java.lang.invoke.MethodHandles中的东西

public MethodHandle unreflect(Method m) throws IllegalAccessException;
Run Code Online (Sandbox Code Playgroud)

但相反的方式呢?

假设我想创建动态java.lang.reflect.Method.这是违法的

public final
   class Method extends AccessibleObject implements GenericDeclaration,
                                                 Member ;
Run Code Online (Sandbox Code Playgroud)

所以,如果我想使用JDK动态代理,我必须使用一些接口(例如成员).虽然有2个主要的抽屉.一,方法如

public Class<?>[] getParameterTypes();
Run Code Online (Sandbox Code Playgroud)

public Class<?> getReturnType();
Run Code Online (Sandbox Code Playgroud)

不是任何界面的一部分,而它们被广泛使用.

第二个缺点是它无法提供直接替换.也就是说,我无法将我的动态代理传递给期望java.lang.reflect.Method的代码.

另一种方法是使用CGLIB或Javaassist.AFAIK,CGLIB无法代理最终班级,是吗?Javaassist可以代理最终类吗?如何从班级中"删除"最终标识符?AFAIL,Javvassist可以某种方式做到......

Raf*_*ter 9

这取决于您需要什么样的代理.基本上有三种方法可以实现这一点,其中两种在生产代码中是可行的.正如@probrekely所说,cglib或javassist的问题在于它们动态地创建了一个子类,这是最终类不可能的.您可以通过以下方式避免此

  • 禁用字节码验证.Java运行时验证字节代码,以确保不加载恶意字节代码.例如,当通过网络或互联网接收课程(例如小程序)时,这很重要.这样,您可以创建最终类的子类,因为字节代码验证程序不会阻止您.假设您可以在仅运行受信任的代码时禁用此验证.这可以通过运行来完成:

    java -Xverify:none ApplicationName
    
    Run Code Online (Sandbox Code Playgroud)

    然而,这是我建议你至少的解决方案.我不会将此aproach用于生产代码,但它肯定是最容易实现的解决方案.

  • final加载类之前或之后,从加载的类中删除修饰符.这可以通过使用Java代理来实现.Java代理可以在应用程序启动时通过命令行安装,也可以在运行时通过Attach API安装.使用像ASM这样的字节代码工具,您可以解析原始字节数组并从所有感兴趣的类中删除最终修饰符.也可以重新定义已加载的类.删除final修饰符不会引入与旧类版本的冲突,因此始终可以进行重新定义.

  • 与我描述的删除final修饰符相同,但重新定义加载的类以实际包含原始类中的所有检测逻辑.这个问题最需要付出最大努力,但这将使您的仪器转换为所有其他代码.这将是所有解决方案中最干净的解决方案.


pob*_*key 0

抱歉,您想要的不可能:

您可以使用 CGLIB 或 Javassist 为具体类创建代理,因为这些库会动态生成您尝试代理的类的子类。类final不能被子类化,因此不能以这种方式创建代理。

PowerMock 确实允许您代理final类和方法,但这是因为它在其特殊功能下运行您的测试,该特殊ClassLoader功能使用 Javassist 在加载您希望代理的类时修改它们的字节码。(您不会想在生产中使用这种东西,因为通常该类的修改后的“僵尸”版本除了运行特定的模拟单元测试之外没有什么好处。)

然而,PowerMock 方法在这里不起作用 - 您想要代理java.lang.reflect.Method,它位于引导类路径上,因此会在任何 PowerMock/Javassist 类型工具之前加载,因此不可代理。