Java中的动态接口代理

3

我正在尝试将一些C#代码移植到Java中,但是代理实现方面遇到了问题。

我有这个类:

public class Comic
{
    public int ComicID;

    private List<Volume> _volumes;
    public List<Volume> GetVolumes()
    {
        return _volumes;
    }
    public void SetVolumes(List<Volume> volumes)
    {
        _volumes = volumes;
    }
}

我正在尝试为该实体的特定方法调用添加拦截器,但我还需要访问它的字段,因为我将使用它们。

在查找如何在Java中实现代理时,我得到了这个:

public void Load(Class<?> type)
{
    // - type is a Comic.class 

    ClassLoader appLoader = this.getClass().getClassLoader();

    MyProxyHandler proxyHandler = new MyProxyHandler();
    Object proxy = Proxy.newProxyInstance(appLoader, new Class[] { ComicInterface.class }, proxyHandler);
}

问题是,类型是动态的,所以我不知道将会是什么,而且我不想让代码要求拥有所有东西的接口,所以我查了一下如何 构建动态接口:
public class InterfaceLoader extends ClassLoader
{
    public InterfaceLoader(ClassLoader loader)
    {
        super(loader);
    }

    public Class<?> GetInterface(Class<?> type) throws Exception
    {
        String interfaceName = type.getName() + "$Proxy";
        Class<?> interfaceType = null;
        interfaceType = findLoadedClass(interfaceName);
        if (interfaceType != null) { return interfaceType; }

        // - According to the link
        byte[] classData = new InterfaceBuilder().BuildInterface(interfaceName, type.getDeclaredMethods());
        return defineClass(interfaceName, classBytes, 0, classBytes.length);
    }
}

然后我会与代理一起使用它:
public void Load(Class<?> type)
{
    // - type is a Comic.class 
    ClassLoader appLoader = this.getClass().getClassLoader();

    InterfaceLoader loader = new InterfaceLoader(appLoader);
    Class<?> dynamicInterface = loader.GetInterface(type);
    // - dynamicInterface on debug is "console.Comic$Proxy", seems fine

    MyProxyHandler proxyHandler = new MyProxyHandler();
    Object proxy = Proxy.newProxyInstance(appLoader, new Class[] { dynamicInterface }, proxyHandler);
}

我得到的异常是

java.lang.IllegalArgumentException: interface console.Comic$Proxy is not visible from class loader

我查找了该异常但只找到两种解决方案,一是确保名称不与任何类型冲突(我确定没有),另一个是使用this.getClass().getClassLoader()而非type.getClass().getClassLoader()(同样的错误)。我错在哪了?
还有另一个问题,如何获取“原始”的对象,以便可以从字段(例如ComicID)中获取/设置值?使用上述代理方法,我可以拦截方法,但无法访问其字段。
我已经阅读了这里的信息来获取InvocationHandler,但我不知道如何从处理程序获取对象,我也找不到相关的示例。

不要在代码中掩盖实际问题。 - user207421
你实际上想要在这里实现什么?很可能有更简单或者已存在的方法来处理它。 - chrylis -cautiouslyoptimistic-
@chrylis 这是从C#代码移植过来的,我生成了一个“副本”对象,继承了原始对象,但增加了拦截任何方法调用的能力(例如用于日志记录或延迟加载)。现在想想,我发现Java有一个动态代理构建器,但由于它只适用于接口,所以与创建继承原始类的动态类不同... - Danicco
@Daniichi,听起来你需要的是像AspectJ这样的东西,或者可能是Spring AOP(如果代理可接受的话)。无论是AspectJ还是基于cglib的AOP都可以与实际类一起使用,只要所涉及的类和方法不是final的即可。 - chrylis -cautiouslyoptimistic-
1个回答

0

只有一个ClassLoader可以看到您动态生成的接口,那就是InterfaceLoader加载器。您可能可以通过将其传递给newProxyInstance来消除错误。

但是,我很确定这对您没有任何好处。您编写的所有Java代码都不是由该类加载器加载的,因此您将无法通过该接口进行调用,除非使用反射。

如果您想使用代理,则必须使Comic实现一个接口,然后在所有地方使用该接口。在这种情况下,您下一个问题的答案是将原始对象的实例传递给代理处理程序构造函数。

如果您真的想要做这个动态拦截的事情,您可能需要考虑使用ASM(http://asm.ow2.org/)或Javaassist来构建Comic的动态子类,以覆盖您想要拦截的方法,而不是使用代理。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接