HttpMessageConverter与自定义HttpMessageConverter

HttpMessageConverter(消息转换器),POJO转JSON之原理解析内容协商原理(1)都有涉及到,本篇将稍作总结,并实现一个自定义的消息转换器。

HttpMessageConverter的定义
public interface HttpMessageConverter<T> {
	boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);

	boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);

	List<MediaType> getSupportedMediaTypes();

	default List<MediaType> getSupportedMediaTypes(Class<?> clazz) {
		return (canRead(clazz, null) || canWrite(clazz, null) ?
				getSupportedMediaTypes() : Collections.emptyList());
	}

	T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
			throws IOException, HttpMessageNotReadableException;

	void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
			throws IOException, HttpMessageNotWritableException;
}
HttpMessageConverter的实现类

HttpMessageConverter的实现类挺多的,如

  • MappingJackson2HttpMessageConverter,可以将POJO转换为JSON,作为响应返回给浏览器。
  • MappingJackson2XmlHttpMessageConverter,可以将POJO转换为XML,作为响应返回给浏览器。
自定义消息转换器
创建基础项目
  1. 新建一个spring项目:helloworld。
    Group:com.example
    Artifact:helloworld
    Package name:com.example.boot
    添加依赖:Lombok、Spring Web和Spring Configuration Processor。其中,spring-boot-starter-web>spring-boot-starter-jackson里的jackson jar能够返回JSON。
    继续添加依赖:jackson-dataformat-xml,它可以返回XML。
<dependencies>
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-configuration-processor</artifactId>
          <optional>true</optional>
      </dependency>
      <dependency>
          <groupId>org.projectlombok</groupId>
          <artifactId>lombok</artifactId>
          <optional>true</optional>
      </dependency>
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-test</artifactId>
          <scope>test</scope>
      </dependency>
      <dependency>
          <groupId>com.fasterxml.jackson.dataformat</groupId>
          <artifactId>jackson-dataformat-xml</artifactId>
          <version>2.13.1</version>
      </dependency>
</dependencies>
  1. com.example.boot下新建类:bean.Person。
package com.example.boot.bean;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

import java.util.Date;

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Person {
    private String name;
    private Integer age;
    private Date birth;
}
  1. com.example.boot下新建控制器:controller.HelloController。
package com.example.boot.controller;

import com.example.boot.bean.Person;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.Date;

@Controller
public class HelloController {
    @GetMapping("/person")
    @ResponseBody
    public Person person(){
        Person person = new Person("Tom", 1, new Date());
        return person;
    }
}
  1. helloworld根目录下新建目录:http,http目录下新建文件:helloworld.http。
GET localhost:8080/person
Accept: application/json
###

GET localhost:8080/person
Accept: application/xml
###
  1. 启动应用,访问接口localhost:8080/person。

Accept: application/json 时,接口返回的信息如下。

GET http://localhost:8080/person

HTTP/1.1 200 
Content-Type: application/json
Transfer-Encoding: chunked
Date: Fri, 07 Jan 2022 07:23:02 GMT
Keep-Alive: timeout=60
Connection: keep-alive

{
  "name": "Tom",
  "age": 1,
  "birth": "2022-01-07T07:23:02.900+00:00"
}

Accept: application/xml 时,接口返回的信息如下。

GET http://localhost:8080/person

HTTP/1.1 200 
Content-Type: application/xml;charset=UTF-8
Transfer-Encoding: chunked
Date: Fri, 07 Jan 2022 07:26:42 GMT
Keep-Alive: timeout=60
Connection: keep-alive

<Person>
    <name>Tom</name>
    <age>1</age>
    <birth>2022-01-07T07:26:42.502+00:00</birth>
</Person>
自定义消息转换器
  1. com.example.boot下新建消息转换器:converter.HelloMessageConverter。
package com.example.boot.converter;

import com.example.boot.bean.Person;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;

import java.io.IOException;
import java.io.OutputStream;
import java.util.List;

public class HelloMessageConverter implements HttpMessageConverter<Person> {
    @Override
    public boolean canRead(Class<?> clazz, MediaType mediaType) {
        return false;
    }

    @Override
    public boolean canWrite(Class<?> clazz, MediaType mediaType) {
        return clazz.isAssignableFrom(Person.class);
    }

    @Override
    public List<MediaType> getSupportedMediaTypes() {
        return MediaType.parseMediaTypes("application/x-helloworld");
    }

    @Override
    public Person read(Class<? extends Person> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
        return null;
    }

    @Override
    public void write(Person person, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
        String data = person.getName()+";"+person.getAge()+";"+person.getBirth();
        OutputStream body = outputMessage.getBody();
        body.write(data.getBytes());
    }
}
  1. com.example.boot下新建配置类:config.MyConfig。
package com.example.boot.config;

import com.example.boot.converter.HelloMessageConverter;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

@Configuration
public class MyConfig implements WebMvcConfigurer {
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(new HelloMessageConverter());
    }
}
  1. helloworld.http中新增接口且Accept字段值为自定义类型:application/x-helloworld
GET localhost:8080/person
Accept: application/json
###

GET localhost:8080/person
Accept: application/xml
###

GET localhost:8080/person
Accept: application/x-helloworld
###
  1. 启动应用,访问接口localhost:8080/person。

Accept: application/x-helloworld 时,接口返回的信息如下。

GET http://localhost:8080/person

HTTP/1.1 200 
Content-Length: 34
Date: Fri, 07 Jan 2022 07:50:36 GMT
Keep-Alive: timeout=60
Connection: keep-alive

Tom;1;Fri Jan 07 15:50:36 CST 2022

想了解SpringMVC底层所有的消息转换器是怎么配置的,可以查看或调试以下源码。

  • WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter#configureMessageConverters
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
	this.messageConvertersProvider
			.ifAvailable((customConverters) -> converters.addAll(customConverters.getConverters()));
}
  • HttpMessageConverters#getConverters和HttpMessageConverters#getDefaultConverters
public HttpMessageConverters(boolean addDefaultConverters, Collection<HttpMessageConverter<?>> converters) {
	List<HttpMessageConverter<?>> combined = getCombinedConverters(converters,
			addDefaultConverters ? getDefaultConverters() : Collections.emptyList());
	//...
}

public List<HttpMessageConverter<?>> getConverters() {
	return this.converters;
}

private List<HttpMessageConverter<?>> getDefaultConverters() {
	List<HttpMessageConverter<?>> converters = new ArrayList<>();
	if (ClassUtils.isPresent("org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport",
			null)) {
		converters.addAll(new WebMvcConfigurationSupport() {

			public List<HttpMessageConverter<?>> defaultMessageConverters() {
				return super.getMessageConverters();
			}

		}.defaultMessageConverters());
	}
	//...
}

  • WebMvcConfigurationSupport#getMessageConverters
protected final List<HttpMessageConverter<?>> getMessageConverters() {
	if (this.messageConverters == null) {
		this.messageConverters = new ArrayList<>();
		configureMessageConverters(this.messageConverters);
		if (this.messageConverters.isEmpty()) {
			addDefaultHttpMessageConverters(this.messageConverters);
		}
		extendMessageConverters(this.messageConverters);
	}
	return this.messageConverters;
}
  • WebMvcConfigurationSupport#addDefaultHttpMessageConverters
//WebMvcConfigurationSupport
public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {
	private static final boolean jackson2Present;
	private static final boolean jackson2XmlPresent;
	static {
		jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) &&
				ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
		jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader);
	}
	@Bean
	public RequestMappingHandlerAdapter requestMappingHandlerAdapter(
			@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
			@Qualifier("mvcConversionService") FormattingConversionService conversionService,
			@Qualifier("mvcValidator") Validator validator) {
		if (jackson2XmlPresent) {
			messageConverters.add(new MappingJackson2XmlHttpMessageConverter(builder.build()));
		}
		if (jackson2Present) {
			Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.json();

			messageConverters.add(new MappingJackson2HttpMessageConverter(builder.build()));
		}
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值