如何使用ByteBuddy向现有实例添加字段?

问题描述 投票:1回答:1

我需要从Spring应用程序将文档扔到MongoDB实例,在这里我可以利用其数据包中的MongoTemplate。

但是Spring将这些实例id字段用作MongoDB文档ID,导致数据库中的ID重复,从而避免了重复的实例。

这是在项目中,其中:

  • 我需要将数据扔给MongoDB实例,以便以后从数据库本身而不是从应用程序进行分析(所以我不在乎是否无法向其查询);
  • 可能存在重复文档,并且是要求的一部分;
  • 文档是对Web服务请求的反序列化,我可以在类定义中添加_id字段,但是将来从WSDL生成的源代码将丢弃该字段;
  • 在初始阶段以这种方式将文档用于测试目的,在收集和分析了一些数据之后,它们不会发送到数据库中

this question读取,我发现id字段对于Spring是必填的,我需要以某种方式添加_id字段。

这是我将文档插入集合的方式:

public void save(List<MyDocument> docs) {
    mongoTemplate.insert(MyDocument.class).inCollection("docscoll").all(docs);
}

我对ByteBuddy完全陌生(我认为这可能是适合该工作的库,但是可以建议另一个库),通过在此处进行搜索,我将这段代码组合在一起:

new ByteBuddy()
  .redefine(MyDocument.class)
  .defineField("_id", int.class, Visibility.PUBLIC)
  .make()
  .load(MyDocument.class.getClassLoader(), ClassLoadingStrategy.Default.INJECTION);

但是失败了:

Cannot inject already loaded type: class com.MyDocument
java spring byte-buddy mongotemplate
1个回答
1
投票

这不起作用。大多数JVM不允许将字段添加到已加载的类中。因此,尽管Byte Buddy可以调整字节码,但即使在加载类后正确完成了操作,也无法正常工作。正确的方法还需要Java代理,例如,可以使用Byte Buddy Agent项目来附加Java代理:

new ByteBuddy()
  .redefine(MyDocument.class)
  .defineField("_id", int.class, Visibility.PUBLIC)
  .make()
  .load(MyDocument.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent());

但是由于VM的限制,这将不起作用,您也可以更改方法的内容。

您可以在应用程序启动之前附加Java代理以添加字段,而该类尚未加载。这可以使用Java代理来完成,并且Byte Buddy可以轻松实现这样的代理:

new AgentBuilder.Default()
  .type(named("<package>.MyDocument"))
  .transform((builder, typeDescription, classLoader, module) -> builder
    .defineField("_id", int.class, Visibility.PUBLIC))
  .installOn(<instrumentation>);

这样,如果您在代理的premain方法中添加此代码,则会在第一次加载类之前添加该字段。

但是,我确实想知道这是否是解决您问题的正确方法。通常,我宁愿使用弱映射而不是字节码检测。

© www.soinside.com 2019 - 2024. All rights reserved.