一、自定义spring mvc protostuff消息转换器
1.protostuff是一个序列化和反序列化的高性能类型,它比json字节数少,且快,适合做rpc调用
2.自定义protostuff消息转换器,代码如下:
public class ProtostuffHttpMessageConverter extends AbstractHttpMessageConverter<Object> {
/**
* 避免每次序列化都重新申请Buffer空间
*/
private static LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
/**
* 缓存Schema
*/
private static Map<Class<?>, Schema<?>> schemaCache = new ConcurrentHashMap<>();
public ProtostuffHttpMessageConverter() {
super(new MediaType("application", "xxx-protostuff", Charset.forName("UTF-8")));
}
@Override
protected boolean supports(Class aClass) {
return true;
}
@Override
protected Object readInternal(Class aClass, HttpInputMessage httpInputMessage) throws IOException, HttpMessageNotReadableException {
InputStream in = httpInputMessage.getBody();
byte[] outByte = IOUtils.toByteArray(in);
return this.deserialize(outByte, aClass);
}
public <T> T deserialize(byte[] data, Class<T> clazz) {
Schema<T> schema = getSchema(clazz);
T obj = schema.newMessage();
ProtostuffIOUtil.mergeFrom(data, obj, schema);
return obj;
}
@Override
protected void writeInternal(Object o, HttpOutputMessage httpOutputMessage) throws IOException, HttpMessageNotWritableException {
httpOutputMessage.getBody().write(this.serialize(o));
}
private <T> byte[] serialize(T obj) {
Class<T> clazz = (Class<T>) obj.getClass();
Schema<T> schema = getSchema(clazz);
byte[] data;
try {
data = ProtostuffIOUtil.toByteArray(obj, schema, buffer);
} finally {
buffer.clear();
}
return data;
}
private static <T> Schema<T> getSchema(Class<T> clazz) {
Schema<T> schema = (Schema<T>) schemaCache.get(clazz);
if (Objects.isNull(schema)) {
//这个schema通过RuntimeSchema进行懒创建并缓存
//所以可以一直调用RuntimeSchema.getSchema(),这个方法是线程安全的
schema = RuntimeSchema.getSchema(clazz);
if (Objects.nonNull(schema)) {
schemaCache.put(clazz, schema);
}
}
return schema;
}
}
|
①在构造方法中定义一个支持的媒体类型:application/xxx-protostuff
②readInternal方法用protostuff对输入的参数做反序列化工作
③writeInternal方法使用protostuff对返回的对象做序列化操作
3.注册自定义的消息转换器到spring中,代码如下:
@Configuration
public class MvcConfig extends WebMvcConfigurationSupport {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(protostuffHttpMessageConverter());
}
@Bean
public ProtostuffHttpMessageConverter protostuffHttpMessageConverter() {
return new ProtostuffHttpMessageConverter();
}
}
|
4.暴露一个服务接口,供下面的的feign调用,代码如下:
@Api(tags = "protostuff消息转换器服务")
@RestController
@RequestMapping(ApiConstants.PATH + "/protostuff")
public class ProtostuffController {
@ApiOperation(value = "反序列化protostuff二进制数据")
@PostMapping(value = "/deserialize")
public ResponseVO<?> deserialize(@RequestBody ProtostuffEntity entity) {
return ResponseVO.ok(entity);
}
@ApiOperation(value = "反序列化protostuff二进制数据1")
@PostMapping(value = "/deserialize1")
public ResponseVO<?> deserialize1(@RequestBody ProtostuffEntity entity) {
return ResponseVO.ok(entity);
}
}
|
二、自定义feign protostuff消息转换器
1.自定义feign protostuff消息转换器,代码如下:
public class ProtostuffHttpMessageConverter implements GenericHttpMessageConverter<Object> {
/**
* 避免每次序列化都重新申请Buffer空间
*/
private static LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
/**
* 缓存Schema
*/
private static Map<Class<?>, Schema<?>> schemaCache = new ConcurrentHashMap<>();
@Override
public boolean canRead(Type type, Class<?> aClass, MediaType mediaType) {
if (Objects.isNull(mediaType)) {
return false;
}
MediaType supportType = new MediaType("application", "xxx-protostuff", Charset.forName("UTF-8"));
return supportType.toString().startsWith(mediaType.toString());
}
@Override
public boolean canRead(Class<?> aClass, MediaType mediaType) {
if (Objects.isNull(mediaType)) {
return false;
}
MediaType supportType = new MediaType("application", "xxx-protostuff", Charset.forName("UTF-8"));
return supportType.toString().startsWith(mediaType.toString());
}
@Override
public boolean canWrite(Class<?> aClass, MediaType mediaType) {
if (Objects.isNull(mediaType)) {
return false;
}
MediaType supportType = new MediaType("application", "xxx-protostuff", Charset.forName("UTF-8"));
return supportType.toString().startsWith(mediaType.toString());
}
@Override
public List<MediaType> getSupportedMediaTypes() {
return Stream.of(new MediaType("application", "xxx-protostuff", Charset.forName("UTF-8"))).collect(Collectors.toList());
}
@Override
public Object read(Class<?> aClass, HttpInputMessage httpInputMessage) throws IOException, HttpMessageNotReadableException {
return readInternal(aClass, httpInputMessage);
}
@Override
public void write(Object o, MediaType mediaType, HttpOutputMessage httpOutputMessage) throws IOException, HttpMessageNotWritableException {
writeInternal(o, httpOutputMessage);
}
@Override
public Object read(Type type, Class<?> aClass, HttpInputMessage httpInputMessage) throws IOException, HttpMessageNotReadableException {
ParameterizedTypeImpl type1 = (ParameterizedTypeImpl) type;
return readInternal(type1.getRawType(), httpInputMessage);
}
@Override
public boolean canWrite(Type type, Class<?> aClass, MediaType mediaType) {
if (Objects.isNull(mediaType)) {
return false;
}
MediaType supportType = new MediaType("application", "xxx-protostuff", Charset.forName("UTF-8"));
return supportType.toString().startsWith(mediaType.toString());
}
@Override
public void write(Object o, Type type, MediaType mediaType, HttpOutputMessage httpOutputMessage) throws IOException, HttpMessageNotWritableException {
writeInternal(o, httpOutputMessage);
}
protected Object readInternal(Class aClass, HttpInputMessage httpInputMessage) throws IOException, HttpMessageNotReadableException {
InputStream in = httpInputMessage.getBody();
byte[] outByte = IOUtils.toByteArray(in);
return this.deserialize(outByte, aClass);
}
public <T> T deserialize(byte[] data, Class<T> clazz) {
Schema<T> schema = getSchema(clazz);
T obj = schema.newMessage();
ProtostuffIOUtil.mergeFrom(data, obj, schema);
return obj;
}
protected void writeInternal(Object o, HttpOutputMessage httpOutputMessage) throws IOException, HttpMessageNotWritableException {
httpOutputMessage.getBody().write(this.serialize(o));
}
private <T> byte[] serialize(T obj) {
Class<T> clazz = (Class<T>) obj.getClass();
Schema<T> schema = getSchema(clazz);
byte[] data;
try {
data = ProtostuffIOUtil.toByteArray(obj, schema, buffer);
} finally {
buffer.clear();
}
return data;
}
private static <T> Schema<T> getSchema(Class<T> clazz) {
Schema<T> schema = (Schema<T>) schemaCache.get(clazz);
if (Objects.isNull(schema)) {
//这个schema通过RuntimeSchema进行懒创建并缓存
//所以可以一直调用RuntimeSchema.getSchema(),这个方法是线程安全的
schema = RuntimeSchema.getSchema(clazz);
if (Objects.nonNull(schema)) {
schemaCache.put(clazz, schema);
}
}
return schema;
}
}
|
①fegin中统一解码的入口类是SpringEncoder,SpringEncoder获取的converter需要继承GenericHttpMessageConverter类才可生效,否则获取到的responseClass为null,会报no suitable HttpMessageConverter found for response type错误
②canRead和canWirte方法中对媒体类型做判断,如果为application/xxx-protostuff媒体类型,就返回true
2.注册自定义的feign protostuff消息转换,如上面3.注册相同
3.写一个测试fegin client,调用上面的服务,代码如下:
@FeignClient(value = "tsc", url = "http://localhost:8099/api/tsc/protostuff")
public interface ProtostuffFeignClient {
@ApiOperation(value = "protostuff二进制数据")
@PostMapping(value = "/deserialize", consumes = {"application/xxx-protostuff"}, produces = {"application/xxx-protostuff"})
ResponseVO<ProtostuffEntity> deserialize(@RequestBody ProtostuffEntity entity);
@ApiOperation(value = "反序列化protostuff二进制数据1")
@PostMapping(value = "/deserialize1")
public ResponseVO<ProtostuffEntity> deserialize1(@RequestBody ProtostuffEntity entity);
}
|
4.写一个测试调用的controller,调用上面的feign服务,代码如下:
@RestController
@RequestMapping(ApiConstants.PATH + "/rpc-client")
public class ProtostuffClientController {
@Autowired
private ProtostuffFeignClient feignClient;
@ApiOperation(value = "调试protostuff")
@GetMapping(value = "/protostuff")
public ResponseVO<?> query() {
ProtostuffEntity entity = new ProtostuffEntity();
entity.setDesc("protostuff");
entity.setId("protostuff-xxx");
ResponseVO<ProtostuffEntity> ret = feignClient.deserialize(entity);
return ResponseVO.ok(ret);
}
}
|
本文介绍如何在SpringMVC和Feign中自定义Protostuff消息转换器,包括实现高性能序列化与反序列化的方法,并提供具体的代码实现。
167万+

被折叠的 条评论
为什么被折叠?



