使用引用获取 Java 个对象

Get Java Object Using A Reference

我有这个问题,我非常需要找到答案。

我的问题:如何获取对象的引用(类似于内存地址)然后使用引用获取对象(注意:我知道对象位于何处(在数组))。我听说有一些方法可以使用 Unsafe class 甚至 JNA 来做我想做的事情(我对两者都缺乏了解他们,但很高兴学习!)

我在做什么:

我制作了一个应用程序 (app1),它通过 Instrumentation 插入到另一个应用程序**(app2)** 和这个应用程序 (app1) 将通过反射 API 从 (app2) 获取的数据通过 RMI 发送到我的另一个应用程序 (app3)。数组中的对象在值方面不断变化,所以我正在做的是获取对象并读取它们的数据但是当我尝试使用通常由 [=12 完成的先前获得的对象来获取更新数据时,我的问题就来了=] 但我无法将先前获得的对象保留在当前应用程序 (app1) 上,因为它是不可序列化的,因此我需要获取该对象的引用以便稍后获取它。

我试过的:

我已经尝试使用 HashCode 来完成它,但是只要对象的值发生变化并且 hashCode 可能与其他对象相同,HashCode 就会保持 changing/dissppears,因此这会产生另一个问题。

我试过使用 XStream、GSON 等来实现它,但使用 xml/json 它会创建一个新对象,并且与之前获得的对象无关。因此,当我尝试使用 JSON/XML 创建的对象获取有关该对象的更新信息时,它 return 是旧信息,因为它不等于先前获得的对象。所以像 getDeclaredField(fieldName).get(PreviouslyObtainedObject) returns 更新了关于对象的信息但是 getDeclaredField(fieldName).get(JSON/XMLVersionOfPreviouslyObtainedObject) 没有 return 新数据。

由于对象不可序列化,我无法执行对象 -> 字节数组。

问题中没有代码,因为我觉得代码对问题没有意义。要获得有关我在做什么的更多信息,您可以查看我之前的一个问题: 注意:这个问题和我之前的问题有所不同,因为之前的问题讨论了将对象包装或转换为 JSON 或 XML 格式的方法,而这个只是为了获得参考。

我正在尝试做的事情的总结(为了更清楚):

我有 2 个申请: 第一个应用程序 (app1):被检测到另一个应用程序的应用程序(收集数据并发送)

第二个应用程序 (app2):从检测数据接收数据并执行逻辑的应用程序..

app1 通过反射 API 从应用程序中获取数据,例如 (getDeclaredField(fieldName).get(object/null);) app1 将对象引用(内存 address/hashCode/this 是我想要的)和对象的当前值发送到 app2app2 随时发回引用并获取对象更新数据。

在代码方面很像:

我第一次想要获取一个对象时,我使用反射访问一个对象数组,然后循环遍历它以找到我的完美对象,如下所示:

Object[] obArray = (Object[]) getClassLoader().loadClass("className").getDeclaredField("fieldName").get(null);
for(Object ob : obArray) {
    if(ob is what I want) return ob;
}

然后如果我想获取 ob(我上面获取的对象)的更新信息。我做 getClassLoader().loadClass(className).getDeclaredField(fieldName).get(ob);

现在由于我处理的对象数量太多,我无法存储我在同一个应用程序上获取的对象,所以我必须获得对它们的引用(类似于内存地址或哈希码)以发送到 app2 以便 app2 可以在稍后将其发送回 app1 以取回对象并获取更新信息关于它。

当您使用 RMI 时,您已经在使用一个跨 JVM 维护引用的框架。但它不支持在将引用传递给另一个 JVM 并返回后检索原始本地对象引用,因为它只允许通过远程接口与引用的对象进行通信。

但由于您的实际目标是允许读取引用对象的字段,因此您可以通过远程接口导出此操作。

下面是这种访问的示意图。请注意,出于演示目的,它包含一些您不会在生成代码中执行的操作(例如将 sun.rmi.dgc.server.gcInterval 设置为非常小的值)。

public class RmiApp implements RmiAppB {
    public static void main(String[] args) throws Exception {
        System.setProperty("sun.rmi.dgc.server.gcInterval", "1000");
        if(args.length == 0) runAppB(); else runAppA();
    }

    private static void runAppA() throws Exception {
        String me = String.format("App A (pid%6d) ", ProcessHandle.current().pid());
        System.out.println(me + "started");
        Registry r = LocateRegistry.getRegistry(12348);
        RmiAppB appB = (RmiAppB)r.lookup("foo");
        InnocuousObject obj = new InnocuousObject("a local string", 42, 1.234f);
        System.out.println(me + "sending reference");
        appB.passTheReference((WrappingReference)
            UnicastRemoteObject.exportObject(new WrappingReferenceImpl(obj), 0));

        LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
        System.out.println(me + "changing fields");
        obj.field1 = "another string";
        obj.field2 = 0;
        obj.field3 = 0.1f;
        obj = null; // we don't need to keep a strong reference

        LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(5));

        System.exit(0);
    }

    private static void runAppB() throws Exception {
        System.out.printf("App B (pid%6d) started%n", ProcessHandle.current().pid());
        Registry r = LocateRegistry.createRegistry(12348);
        r.bind("foo", UnicastRemoteObject.exportObject(new RmiApp(), 0));
        //UnicastRemoteObject.exportObject(new RmiApp(), 12345);
        new ProcessBuilder(
            Paths.get(System.getProperty("java.home"), "bin", "java").toString(),
            "-cp", System.getProperty("java.class.path"),
            RmiApp.class.getName(), "runA")
            .inheritIO().start().waitFor();
        System.exit(0);
    }

    @Override
    public void passTheReference(WrappingReference o) throws RemoteException {
        String me = String.format("App B (pid%6d) ", ProcessHandle.current().pid());
        System.out.println(me + "received object");
        System.out.println(me + "querying field1 " + o.getField("field1"));
        System.out.println(me + "querying field2 " + o.getField("field2"));
        System.out.println(me + "querying field3 " + o.getField("field3"));
        CompletableFuture.runAsync(() -> {
            LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(3));
            try {
                System.out.println(me + "querying field1 " + o.getField("field1"));
                System.out.println(me + "querying field2 " + o.getField("field2"));
                System.out.println(me + "querying field3 " + o.getField("field3"));
            } catch(RemoteException ex) {
                ex.printStackTrace();
            }
        });
    }
}

interface RmiAppB extends Remote {
    void passTheReference(WrappingReference o) throws RemoteException;
}

interface WrappingReference extends Remote {
    Object getField(String nra) throws RemoteException;
}

class WrappingReferenceImpl implements WrappingReference {
    InnocuousObject referent;

    WrappingReferenceImpl(InnocuousObject referent) {
        this.referent = referent;
    }

    @Override
    public Object getField(String nra) throws RemoteException {
        try {
            return referent.getClass().getDeclaredField(nra).get(referent);
        } catch(NoSuchFieldException|IllegalAccessException ex) {
            throw new RemoteException(null, ex);
        }
    }
}

class InnocuousObject {
    String field1;
    int field2;
    float field3;

    public InnocuousObject(String field1, int field2, float field3) {
        this.field1 = field1;
        this.field2 = field2;
        this.field3 = field3;
    }

    @Override
    protected void finalize() throws Throwable {
        System.out.println("InnocuousObject.finalize()");
    }

    @Override
    public String toString() {
        return field1;
    }
}

在一个 运行 中,它打印了

App B (pid  8924) started
App A (pid  9132) started
App A (pid  9132) sending reference
App B (pid  8924) received object
App B (pid  8924) querying field1 a local string
App B (pid  8924) querying field2 42
App B (pid  8924) querying field3 1.234
App A (pid  9132) changing fields
App B (pid  8924) querying field1 another string
App B (pid  8924) querying field2 0
App B (pid  8924) querying field3 0.1
InnocuousObject.finalize()

显示远程引用如何在另一个 JVM 持有远程引用时使对象保持活动状态,但在不存在远程引用时允许其垃圾回收。

进一步注意重要的一点,即目标 InnocuousObject 不知道通过执行反射访问的封装 WrappingReferenceImpl 进行的远程访问。这似乎支持您让 app1 检测 app2 而只有 app1 知道远程访问的情况。