Как я могу добавить свой собственный код в сгенерированные JAVA классы из файла proto?
Я использую protobuf и создаю классы JAVA из следующего файла proto.
syntax = "proto3";
enum Greeting {
NONE = 0;
MR = 1;
MRS = 2;
MISS = 3;
}
message Hello {
Greeting greeting = 1;
string name = 2;
}
message Bye {
string name = 1;
}
option java_multiple_files = true;
Теперь мне нужно добавить код к сгенерированным файлам, и я обнаружил, что это возможно с помощью пользовательского плагина ( https://developers.google.com/protocol-buffers/docs/reference/java-generated). Я пытаюсь создать этот плагин в Java, что-то вроде этого.
public class Test {
PluginProtos.CodeGeneratorResponse.getDefaultInstance();
/* Code to get generated files from java_out and use the insertion points */
codeGeneratorResponse.writeTo(System.out);
}
А потом бегаю
protoc --java_out=./classes --plugin=protoc-gen-demo=my-plugin --demo_out=. example.proto
Проблема в том, что на моем Test.java
Основной метод, я не знаю, как получить доступ к файлам, созданным опцией --java_out
так что я могу использовать их точки вставки. В настоящее время CodeGeneratorResponse
для экземпляра по умолчанию пуст (нет файлов).
Кто-нибудь знает, как я могу получить CodeGeneratorResponse
из --java_out, чтобы я мог добавить больше кода к сгенерированным классам?
Заранее спасибо.
3 ответа
Недавно я тоже боролся с этим и не смог найти хорошего ответа. Я наконец понял это после того, как некоторое время смотрел на комментарии в сообщении CodeGeneratorResponse.
Поначалу меня оттолкнуло то, что я думал о плагинах как о конвейере, в котором выходные данные одного передаются в следующий. Однако каждый плагин получает одинаковые входные данные (проанализированные файлы.proto, выраженные черезCodeGeneratorRequest
messages), и весь сгенерированный код из плагинов (включая встроенные) объединяется в выходной файл. Однако плагины могут изменять вывод предыдущих плагинов, для чего предназначены точки вставки.
Конкретно к своему вопросу вы должны добавить file
на ответ с name
поле, устанавливаемое на имя сгенерированного файла Java, insertion_point
в поле устанавливается имя точки вставки, в которую вы хотите добавить код, а поле content
в поле устанавливается код, который вы хотите вставить в этот момент.
Я нашел эту статью полезной при создании простого плагина (в данном случае на Python). В качестве простого теста я изменилgenerate_code
функции из этой статьи, чтобы она выглядела так:
def generate_code(request, response):
for proto_file in request.proto_file:
f = response.file.add()
f.name = "Test.java"
f.insertion_point = "outer_class_scope"
f.content = "// Inserting a comment as a test"
Затем я запустил протокол с плагином:
$ cat test.proto
syntax = "proto3";
message MyMsg {
int32 num = 1;
}
$ protoc --plugin=protoc-gen-sample=sample_proto_gen.py --java_out=. --sample_out=. test.proto
$ tail -n3 Test.java
// Inserting a comment as a test
// @@protoc_insertion_point(outer_class_scope)
}
Ваш плагин просто должен быть исполняемым файлом, который читает CodeGeneratorRequest
сообщение от стандартного ввода и пишет CodeGeneratorResponse
сообщение в стандартный вывод, поэтому его можно было бы написать на Java. Я просто выбрал python, так как в целом мне он удобнее, и нашел этот простой пример.
Я создал собственный плагин для Python. Чтобы запустить мой плагин, я использую следующую команду:
protoc --plugin=protoc-gen-custom=my_plugin_executable_file --custom_out=./build test.proto
Итак, я думаю, что вам нужно сгенерировать исполняемый файл из вашего.java файла и использовать его в своей команде.
Непонятно, какой тип Java-кода вы хотите добавить,
но согласно документации protobuf , сообщения являются лишь неизменяемыми держателями данных,
Если вы хотите добавить более богатое поведение в сгенерированный класс, лучший способ сделать это — обернуть сгенерированный класс буфера протокола в класс, специфичный для приложения.
То есть создайте класс-декоратор с экземпляром сообщения и добавьте туда свой собственный Java-код, используя MyMessage.Builder в конечном итоге для создания нового экземпляра измененного сообщения.
public class MyMessageDecorator {
private MyMessage message;
public MyMessageDecorator(MyMessage message) {
super();
this.mmessage=messsage;
}
//access to the message
public MyMessage getMessage() {
return message;
}
//add behaviour
public <result> addBehaviour(<params>) {
//my java code here
...
}
}