10

简而言之: 1. 我有一些最终类,我想为其创建动态代理。我该怎么做?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 动态代理,我必须使用一些接口(例如成员)。虽然有两个主要缺点。一、方法如

public Class<?>[] getParameterTypes();

比如

public Class<?> getReturnType();

不是任何接口的一部分,但它们被广泛使用。

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

另一种方法是使用 CGLIB 或 Javaassist。AFAIK,CGLIB 不能代理最终课程,是吗?Javaassist 可以代理最终类吗?如何从课程中“删除”最终标识符?失败,Javvassist 可以以某种方式做到这一点......

4

2 回答 2

13

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

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

    java -Xverify:none ApplicationName
    

    然而,这是我最不推荐给你的解决方案。我不会将这种方法用于生产代码,但它肯定是最容易实现的解决方案。

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

  • 执行与我描述的删除final修饰符相同的操作,但重新定义加载的类以实际包含原始类中的所有检测逻辑。这个 aporach 肯定需要最大的努力,但这将使您的仪器对所有其他代码透明。这将是所有解决方案中最干净的解决方案。

于 2013-12-01T19:43:36.503 回答
0

对不起,你想要的不可能:

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

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

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

于 2013-11-02T00:15:28.047 回答