spring boot restTemplate protobuf(protocal buffer) demo

该博客介绍了如何使用Spring Boot结合protobuf实现RESTful API服务。作者通过一个简单的server和test演示了protobuf作为数据格式的优势,包括序列化效率和文件大小。在遇到浏览器无法解析protobuf数据格式的问题后,作者提供了使用字符串返回的解决方案,并提到了ProtobufHttpMessageConverter在Spring 4.1中对protobuf的支持。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

protobuf 个人理解就是一种数据格式,只不过在序列化后的大小、序列化、反序列化方面更优秀一些。


demo 是用spring boot做了一个server,一个test。server暴露出来的服务是rest的,数据格式就是protobuf。


直接上代码

common:

proto数据结构文件

 syntax = "proto3";

// package comp.protobuf.pb;

 option java_package = "com.protobuf.model";
 
 option java_outer_classname = "UserProto";

 message User {
   int64 id = 1;
   string name = 2;
   message PhoneNumber {
       string number = 1;
     }
     repeated PhoneNumber phone = 4;
     
 }


生成对应语言的类,我这直接用java 的runtime,也可以直接再cmd中操作

package com.protobuf;

import java.io.IOException;

public class GenerateClass {
	
	
	public static void main(String[] args) {
        String protoFile = "user.proto";//  
        String strCmd = "D:/software/protobuf-3.3.0/protoc.exe --java_out=./src/main/java ./src/main/resources/"+ protoFile;  
        try {
            Runtime.getRuntime().exec(strCmd);
        } catch (IOException e) {
            e.printStackTrace();
        }//通过执行cmd命令调用protoc.exe程序  
    }

}

生成的java类文件 UserProto.java




2. server   port:8080

application, controller

package com.protobuf;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.protobuf.model.UserProto;

import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * This example demonstrates serving up REST payloads encoded using
 * <a href="https://developers.google.com/protocol-buffers/">Google Protocol Buffers</a>.
 */
@SpringBootApplication
public class ProtoServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(ProtoServerApplication.class, args);
    }

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

    private UserProto.User user(long id, String name, Collection<String> phones) {
        Collection<UserProto.User.PhoneNumber> pnList =
        		phones.stream().map(e -> UserProto.User.PhoneNumber.newBuilder().setNumber(e).build()).collect(Collectors.toList());

        return UserProto.User.newBuilder().setId(id).setName(name).addAllPhone(pnList).build();
        
    }
        

    @Bean
    UserRepository userRepository() {
        Map<Long, UserProto.User> userM = new ConcurrentHashMap<>();
        Arrays.asList(
                user(1, "Chris", Arrays.asList("1566666","15777777")),
                user(2, "Josh", Arrays.asList("157777777777","158888888888")),
                user(3, "Matt", Arrays.asList("1566666","159999999999999")),
                user(4, "Russ", Arrays.asList("1599999999","15733337"))
        ).forEach(c -> userM.put(c.getId(), c));
        //UserRepository 只有一个抽象方法,可以认为是一个函数接口,所以可以用lambda表达式 表示。
        return userM::get;
    }

}

interface UserRepository {
	UserProto.User findById(long id);
}


@RestController
class UserRestController {

    @Autowired
    private UserRepository userRepository;

    @RequestMapping("/user/{id}")
    UserProto.User user(@PathVariable Integer id) {
        return userRepository.findById(id);
    }
    
    @RequestMapping("/map/{id}")
    Map<String,String> map(@PathVariable Integer id) {
    	Map<String,String> m = new HashMap<String,String>();
		m.put("name", "yonyou");
		m.put("www","yonyou.com");
		return m;
    }
    
    
    
}

3. test    port:8181

application, controller

package com.protobuf;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import com.protobuf.model.UserProto;

import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * This example demonstrates serving up REST payloads encoded using
 * <a href="https://developers.google.com/protocol-buffers/">Google Protocol Buffers</a>.
 */
@SpringBootApplication
public class ProtobufTestApplication {

    public static void main(String[] args) {
        SpringApplication.run(ProtobufTestApplication.class, args);
    }

    @Bean
	RestTemplate restTemplate() {
		RestTemplate rt = new RestTemplate();
		rt.getMessageConverters().add(new ProtobufHttpMessageConverter());
		return rt;
	}
}

@RestController
class UserRestController {

	@Autowired
	private RestTemplate restTemplate;
	
	
	
    @RequestMapping("/user/{id}")
    UserProto.User user(@PathVariable Integer id) {
    	
    	UserProto.User obj = this.restTemplate.getForObject("http://127.0.0.1:8080/user/" + id,
    			UserProto.User.class);
        return obj;
    }
    
    @RequestMapping("/user2/{id}")
    String user2(@PathVariable Integer id) {
    	
    	UserProto.User obj = this.restTemplate.getForObject("http://127.0.0.1:8080/user/" + id,
    			UserProto.User.class);
        return obj.toString();
    }
    
    @RequestMapping("/map/{id}")
    Map<String,String> map(@PathVariable Integer id) {
    	Map m = this.restTemplate.getForObject("http://127.0.0.1:8080/map/" + id, Map.class);
		return m;
    }
    
    
    
}


还有一个本地测试的 Test.java

package com.protobuf;

import java.io.IOException;

import com.protobuf.model.UserProto;
import com.protobuf.model.UserProto.User;

public class Test {
	
	
	public static void main(String[] args) throws IOException {
		
		
        //模拟将对象转成byte[],方便传输
        UserProto.User.Builder builder = UserProto.User.newBuilder();
        builder.setId(1);
        builder.setName("ant");
                
        builder.addPhone(UserProto.User.PhoneNumber.newBuilder().setNumber("15000000"));
        builder.addPhone(UserProto.User.PhoneNumber.newBuilder().setNumber("15111111"));
        
        UserProto.User user = builder.build();
        System.out.println("before :"+ user.toString());

        System.out.println("===========Person Byte==========");
        for(byte b : user.toByteArray()){
            System.out.print(b);
        }
        System.out.println();
        System.out.println(user.toByteString());
        System.out.println("================================");

        //模拟接收Byte[],反序列化成Person类
        byte[] byteArray =user.toByteArray();
        User p2 = user.parseFrom(byteArray);
        System.out.println("after :" +p2.toString());
    }

}


4. 测试

server 直接访问

通过test访问server

报错是因为我后台代码返回给前台的是protobuf的数据格式,前台浏览器解析不了。


换个方式,就可以了,因为返回的是string


注意代码中的resttemplate

 @Bean
	RestTemplate restTemplate() {
		RestTemplate rt = new RestTemplate();
		rt.getMessageConverters().add(new ProtobufHttpMessageConverter());
		return rt;
	}
若是将rt.getMessageConverters().add(new ProtobufHttpMessageConverter()); 注释掉测试,会发现test后台不能解析server返回的结果

报错


ProtobufHttpMessageConverter  是spring 4.1 新增加的支持protobuf格式的数据转换功能。


代码下载:

http://download.youkuaiyun.com/detail/stonexmx/9889649


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值