Краткое описание как подключить Google Protocol Buffers / Proto3 к вашему Spring проекту



Spring Boot: позволяет быстро разрабатывать stand-alone веб приложения которые вы можете «просто» запустить с минимумом настроек.

Google proto3: легкий, гибкий, автоматический механизм сериализации данных.

Эта статья объяснит как быстро соединить эти технологии вместе. Ознакомиться с технологиями вы можете самостоятельно пройдя по ссылкам в материалах.

Для того чтобы создать новый spring-boot проект вы можете воспользоваться Spring Initializr веб сайтом.
Я выбрал Web как dependency, проект генерировал для maven.

Настройка модуля моделей

В модуле моделей будут находится только файлы с расширением *.proto и автоматически сгенерированные java классы. Proto* файлы помещаются в src/main/proto.

Для примера я создал health.proto:

package demo.domain;

option java_package = "demo.domain";
option java_outer_classname = "HealthCheckProtos";

message HealthCheck {

    required string response = 1;
    required string timestamp = 2;
    required string version = 3;

}

На основании которого будет генериться java класс HealthCheckProtos который я буду использовать в Spring контроллере.

Необходимая конфигурация pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<parent>
		<groupId>demo</groupId>
		<artifactId>spring-boot-proto3</artifactId>
		<version>0.0.1-SNAPSHOT</version>
	</parent>

	<artifactId>spring-boot-proto3-domain</artifactId>
	<packaging>jar</packaging>

	<name>spring-boot-proto3-domain</name>
	<description>Domain module</description>

    <properties>
        <protobuf.input.directory>${project.basedir}/src/main/proto</protobuf.input.directory>
        <protobuf.output.directory>${project.build.directory}/generated-sources</protobuf.output.directory>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.googlecode.protobuf-java-format</groupId>
            <artifactId>protobuf-java-format</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- copy protoc binary into build directory -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>2.10</version>
                <executions>
                    <execution>
                        <id>copy-protoc</id>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>copy</goal>
                        </goals>
                        <configuration>
                            <artifactItems>
                                <artifactItem>
                                    <groupId>com.google.protobuf</groupId>
                                    <artifactId>protoc</artifactId>
                                    <version>${protobuf.version}</version>
                                    <classifier>windows-x86_64</classifier>
                                    <type>exe</type>
                                    <overWrite>true</overWrite>
                                    <outputDirectory>${project.build.directory}</outputDirectory>
                                </artifactItem>
                            </artifactItems>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <!-- compile proto buffer files using copied protoc binary -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-antrun-plugin</artifactId>
                <version>1.8</version>
                <executions>
                    <execution>
                        <id>exec-protoc</id>
                        <phase>generate-sources</phase>
                        <configuration>
                            <target>
                                <property name="protoc.filename" value="protoc-${protobuf.version}-windows-x86_64.exe"/>
                                <property name="protoc.filepath" value="${project.build.directory}/${protoc.filename}"/>
                                <chmod file="${protoc.filepath}" perm="ugo+rx"/>
                                <mkdir dir="${protobuf.output.directory}" />
                                <path id="protobuf.input.filepaths.path">
                                    <fileset dir="${protobuf.input.directory}">
                                        <include name="**/*.proto"/>
                                    </fileset>
                                </path>
                                <pathconvert pathsep=" " property="protobuf.input.filepaths" refid="protobuf.input.filepaths.path"/>
                                <exec executable="${protoc.filepath}" failonerror="true">
                                    <arg value="-I"/>
                                    <arg value="${protobuf.input.directory}"/>
                                    <arg value="--java_out"/>
                                    <arg value="${protobuf.output.directory}"/>
                                    <arg line="${protobuf.input.filepaths}"/>
                                </exec>
                            </target>
                        </configuration>
                        <goals>
                            <goal>run</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>build-helper-maven-plugin</artifactId>
                <version>1.9.1</version>
                <executions>
                    <execution>
                        <id>add-classes</id>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>add-source</goal>
                        </goals>
                        <configuration>
                            <sources>
                                <$ource>${protobuf.output.directory}</$ource>
                            </sources>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>


Здесь впринципе все стандартно, кроме: classifier у protoc и protoc.filename у maven-antrun-plugin, они к сожалению привязаны к системе на машине сборки, я собирал на Win X64.

Настройка веб модуля

К молулю подключаются spring boot и доменный модуль с классами proto3:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>demo</groupId>
        <artifactId>spring-boot-proto3</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

	<artifactId>spring-boot-proto3-web</artifactId>
	<packaging>jar</packaging>

	<name>spring-boot-proto3-web</name>
	<description>Web module</description>

	<dependencies>
	<dependency>
	  <groupId>org.springframework.boot</groupId>
	  <artifactId>spring-boot-starter-web</artifactId>
	</dependency>
        <dependency>
            <groupId>demo</groupId>
            <artifactId>spring-boot-proto3-domain</artifactId>
        </dependency>
	</dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

После этого необходимо добавить ProtobufHttpMessageConverter к HttpMessageConverter.

package demo.config;

import java.util.HashMap;
import java.util.List;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
@EnableWebMvc
public class WebConfiguration extends WebMvcConfigurerAdapter {

    /**
     * {@inheritDoc}
     */
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(protobufHttpMessageConverter());
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        configurer.replaceMediaTypes(new HashMap<>()) //
                .favorPathExtension(false) //
                .defaultContentType(ProtobufHttpMessageConverter.PROTOBUF);
    }

    @Bean
    ProtobufHttpMessageConverter protobufHttpMessageConverter() {
        return new ProtobufHttpMessageConverter();
    }

}

Для примера я создал контроллер который бы показывал статус приложения с использованием модели сгенерированной при помощи proto3:


package demo.web;

import java.time.LocalDateTime;

import demo.domain.HealthCheckProtos;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HealthCheckController {

    @Value("${health.controller.response}")
    private String response;
    @Value("${health.controller.version}")
    private String version;

    @RequestMapping("/ping")
    public HealthCheckProtos.HealthCheck get() {
        return HealthCheckProtos.HealthCheck.newBuilder() //
            .setResponse(response) //
            .setTimestamp(LocalDateTime.now().toString()) //
            .setVersion(version) //
            .build();
    }

}


При генерации классов proto3 так же генерирует билдеры которые вы можете использовать для создания модели.

Пример запроса к контроллеру

" alt=«image»/>

Так же добавив header «Accept»:«application/json» вы можете получить тот же ответ сериализованный в json.

Использовался следующий материал:

Исходный код проекта можно скачать тут: bitbucket.org/wickiup/spring-boot-proto3
Быстрый старт со spring boot: projects.spring.io/spring-boot
Быстрый старт с proto3: developers.google.com/protocol-buffers/docs/javatutorial
Поделиться с друзьями
-->

Комментарии (0)