Как добавить поле в существующий экземпляр с помощью ByteBuddy? - PullRequest
1 голос
/ 21 октября 2019

Мне нужно выбросить документы в экземпляр MongoDB из приложения Spring, где я могу использовать MongoTemplate из его пакета данных.

Но Spring принимает эти экземпляры id поля в качестве идентификатора документа MongoDB, в результатев дубликаты идентификаторов в базе данных, таким образом предотвращая повторные экземпляры.

Это в проекте, где:

  • Мне нужно выбросить данные в экземпляр MongoDB для последующего анализа из самой базы данныхне из приложения (поэтому мне все равно, если я не смогу запросить их обратно);
  • дубликаты документов могут существовать и являются частью требований;
  • документы являются десериализацией веб-службызапросы, я мог бы добавить поле _id к определению класса, но будущее поколение источников из WSDL отбросило бы поле; документы
  • обрабатываются таким образом для целей тестирования на этом начальном этапе, они выиграли 'не могут быть отправлены в базу данных после сбора и анализа некоторых данных

Чтение дляm этот вопрос Я вижу, что поле id является обязательным для Spring, мне нужно как-то добавить поле _id.

Вот как я вставляю документы в коллекцию:

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

Я совершенно новичок в ByteBuddy (я думал, что это может быть подходящая библиотека для работы, но не стесняйтесь предложить другую), выполнив поиск по SO, я собрал этот код:

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

1 Ответ

1 голос
/ 23 октября 2019

Это не будет работать. Большинство JVM не позволяют добавлять поле в уже загруженный класс. Таким образом, несмотря на то, что Byte Buddy может корректировать байт-код, это не сработает, даже если все сделано правильно после загрузки класса. Для правильного способа также потребуется агент Java, который можно подключить с помощью проекта Byte Buddy Agent, например:

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

Однако это не сработает из-за ограничений виртуальной машины, вы также можете изменить содержимое метода.

Вы можете присоединить агент 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 метод вашего агента.

Мне, однако, интересно, если это правильный подход к вашей проблеме. Обычно я предпочел бы использовать слабую карту, чем инструментарий байт-кода.

...