1、在 pom.xml
里引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
2、直接使用 new RestTemplate()
使用 RestTemplate
:
示例代码(注意这有坑,下面解释):
String baiduHtml = new RestTemplate().getForObject("https://www.baidu.com", String.class);
System.out.println(baiduHtml);
注:不能直接使用 @Autowired
自动注入 RestTemplate
使用。因为,官网文档有写:
Since RestTemplate instances often need to be customized before being used, Spring Boot does not provide
any single auto-configured RestTemplate bean.
3、使用 RestTemplateBuilder
,官网文档也有写,提供了自动配置的 RestTemplateBuilder
,可以用 RestTemplateBuilder
创建 RestTemplate
:
示例代码:
@Autowired
private RestTemplateBuilder restTemplateBuilder;
@Autowired
private RestTemplate restTemplate;
@Bean
public RestTemplate getRestTemplate() {
return restTemplateBuilder.build();
}
//调用方式:
String baiduHtml = restTemplate.getForObject("https://www.baidu.com", String.class);
System.out.println(baiduHtml);
这样处理之后(在 SpringBootApplication
入口中定义),在任何地方就可以直接使用自动配置的 RestTemplate
了。
4、编写配置类使用:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfig {
@Autowired
private RestTemplateBuilder restTemplateBuilder;
@Bean
public RestTemplate restTemplate(){
return restTemplateBuilder.build();
}
}
然后就可以在其他地方直接使用 @Autowired
注入使用了。
通过以上三种方式(2、3、4)都可以使用 RestTemplate
访问第三方接口,但是上面说的有个坑,在这里:
使用 new RestTemplate()
和 RestTemplateBuilder
创建的 RestTemplate
访问的时候,编码有问题,我使用 new RestTemplate()
访问百度,得到乱码的HTML,但是使用 RestTemplateBuilder
创建的 RestTemplate
访问的时候,确是正常的!!!
来看源码,一探究竟:
通过集成 actuator
查看所有 beans
发现这么一个 Bean
:
"restTemplateBuilder": {
aliases: [ ],
scope: "singleton",
type: "org.springframework.boot.web.client.RestTemplateBuilder",
resource: "class path resource [org/springframework/boot/autoconfigure/web/client/RestTemplateAutoConfiguration.class]",
dependencies: [ ]
}
根据 resource
找到这个 RestTemplateAutoConfiguration
类:
@Configuration
@AutoConfigureAfter({HttpMessageConvertersAutoConfiguration.class})
@ConditionalOnClass({RestTemplate.class})
public class RestTemplateAutoConfiguration {
private final ObjectProvider<HttpMessageConverters> messageConverters;
private final ObjectProvider<RestTemplateCustomizer> restTemplateCustomizers;
public RestTemplateAutoConfiguration(ObjectProvider<HttpMessageConverters> messageConverters, ObjectProvider<RestTemplateCustomizer> restTemplateCustomizers) {
this.messageConverters = messageConverters;
this.restTemplateCustomizers = restTemplateCustomizers;
}
@Bean
@ConditionalOnMissingBean
public RestTemplateBuilder restTemplateBuilder() {
RestTemplateBuilder builder = new RestTemplateBuilder(new RestTemplateCustomizer[0]);
HttpMessageConverters converters = (HttpMessageConverters)this.messageConverters.getIfUnique();
if(converters != null) {
builder = builder.messageConverters(converters.getConverters());
//↑ ↑ ↑ 看这里,猜测编码转换应该是这里加入了转换器
}
List customizers = (List)this.restTemplateCustomizers.orderedStream().collect(Collectors.toList());
if(!CollectionUtils.isEmpty(customizers)) {
builder = builder.customizers(customizers);
}
return builder;
}
}
看这个类上面有注解:
@AutoConfigureAfter({HttpMessageConvertersAutoConfiguration.class})
顾名思义,是在 HttpMessageConvertersAutoConfiguration
这个配置类之后配置此类。继续进入 HttpMessageConvertersAutoConfiguration
类:
@Configuration
@ConditionalOnClass({HttpMessageConverter.class})
@AutoConfigureAfter({GsonAutoConfiguration.class, JacksonAutoConfiguration.class, JsonbAutoConfiguration.class})
@Import({JacksonHttpMessageConvertersConfiguration.class, GsonHttpMessageConvertersConfiguration.class, JsonbHttpMessageConvertersConfiguration.class})
public class HttpMessageConvertersAutoConfiguration {
static final String PREFERRED_MAPPER_PROPERTY = "spring.http.converters.preferred-json-mapper";
private final List<HttpMessageConverter<?>> converters;
public HttpMessageConvertersAutoConfiguration(ObjectProvider<HttpMessageConverter<?>> convertersProvider) {
this.converters = (List)convertersProvider.orderedStream().collect(Collectors.toList());
}
@Bean
@ConditionalOnMissingBean
public HttpMessageConverters messageConverters() {
return new HttpMessageConverters(this.converters);
}
@Configuration
@ConditionalOnClass({StringHttpMessageConverter.class})
@EnableConfigurationProperties({HttpProperties.class})
protected static class StringHttpMessageConverterConfiguration {
private final Encoding properties;
protected StringHttpMessageConverterConfiguration(HttpProperties httpProperties) {
this.properties = httpProperties.getEncoding();
}
@Bean
@ConditionalOnMissingBean
public StringHttpMessageConverter stringHttpMessageConverter() {
StringHttpMessageConverter converter = new StringHttpMessageConverter(this.properties.getCharset());
converter.setWriteAcceptCharset(false);
return converter;
}
}
}
可以看到这个类中有一个 protected
的内部类,里面配置了名为 StringHttpMessageConverter
的 Bean
,他内部使用了 HttpProperties.class
这个配置类里面的配置,再深入 HttpProperties
看看,代码过多,所以省略了很多:
@ConfigurationProperties(
prefix = "spring.http"
)
public class HttpProperties {
private final HttpProperties.Encoding encoding = new HttpProperties.Encoding();
//...省略其他代码
public HttpProperties.Encoding getEncoding() {
return this.encoding;
}
public static class Encoding {
public static final Charset DEFAULT_CHARSET;
private Charset charset;
public Encoding() {
this.charset = DEFAULT_CHARSET;
}
public Charset getCharset() {
return this.charset;
}
//...省略其他代码
static {
DEFAULT_CHARSET = StandardCharsets.UTF_8;
}
//...省略其他代码
}
}
到这里似乎就渐渐明白了,如果使用 RestTemplateBuilder
创建的 RestTemplate
,它默认就是用了 spring.http.encoding.charset
这个配置的编码。在 application.properties
中配置的时候,可以看到这个编码默认就是 UTF-8
,截图如下:
那么我们来实验一下,修改 application.properties
增加配置:
spring.http.encoding.charset=ISO-8859-1
如果不出意外,使用 RestTemplateBuilder
创建的 RestTemplate
访问的时候应该是会是乱码。
@Autowired
private RestTemplate restTemplate;
private String getRestResponse(String url) {
try {
return new RestTemplate().getForObject(url, String.class);
} catch(Exception ex) {
return ex.toString();
}
}
private String getRestResponse2(String url) {
try {
return restTemplate.getForObject(url, String.class);
} catch(Exception ex) {
return ex.toString();
}
}
@Override
public void run(ApplicationArguments applicationArguments) throws Exception {
System.out.println(getRestResponse("https://www.baidu.com"));
System.out.println(getRestResponse2("https://www.baidu.com"));
}
此时,第二种方式获取到的百度源代码和第一种方式获取到的源代码一致,都是乱码,可见直接使用 new RestTemplate()
将使用默认编码 ISO-8859-1
。
那么问题来了,如何在使用 new RestTemplate()
的时候同样是 UTF-8
编码呢?在创建 RestTemplate
之后做一点处理即可,代码如下:
private String getRestResponse(String url) {
try {
RestTemplate rest = new RestTemplate();
for (HttpMessageConverter<?> converter : rest.getMessageConverters()) {
if (converter instanceof StringHttpMessageConverter) {
((StringHttpMessageConverter)converter).setDefaultCharset(Charset.forName("UTF-8"));
break;
}
}
return rest.getForObject(url, String.class);
} catch(Exception ex) {
return ex.toString();
}
}
此时,RestTemplate
乱码问题已经得到解决。
网友评论