提出问题:怎样配置 RestTemplate的参数比较好呢?RestTemplate 有哪些冷门的用法与知识呢? 怎么不启动项目,直接在测试类中 测试 RestTemplate 呢?
解决效果:
0) 2个参数 ClientHttpRequestFactory 和 拦截器 可以配置
1) 支持多次调用 response body的流信息。( 默认 response 只能调用1次getBody() )
2) 记录请求日志,使用 拦截器 ClientHttpRequestInterceptor
3) 更改 request 和response的 header 等信息,也可在拦截器内统一实现
4) 支持异步操作(暂无)
5) 设置超时时间,否则可能会因为网络不稳定而失败而没有明显的通知信息。设置了超时后,会自动 抛出异常,并且被代码拦截后就可以很快知道了。
/**
* @author storm
* @date 2020-11-19 14:31
*/
@Slf4j
@Configuration
public class RestClientConfig {
// 多个 拦截器,注册为Bean,方便后期 调用该方法 返回同一个单例Bean
@Bean
public List<ClientHttpRequestInterceptor> restInterceptor() {
List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
// 作用:更改 request的 header 和uri 等信息,更改response的header等信息
ClientHttpRequestInterceptor header = new ClientHttpRequestInterceptor() {
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
ClientHttpResponse response = execution.execute(request, body);
response.getHeaders().add("Foo", "bar");
return response;
}
};
// log日志,记录请求时耗时和参数信息
ClientHttpRequestInterceptor timer = new ClientHttpRequestInterceptor() {
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
StopWatch watch = new StopWatch();
watch.start();
ClientHttpResponse response = execution.execute(request, body);
watch.stop();
String time = String.valueOf(watch.getTime(TimeUnit.MILLISECONDS));
response.getHeaders().add("cost-milliseconds", time);
log.info("发送rest请求耗时 :{} ms,{} {} headers:{}", time, request.getMethod(), request.getURI(), request.getHeaders());
return response;
}
};
interceptors.add(timer);
interceptors.add(header);
return interceptors;
}
/**
* 支持多次使用 response.getBody() 读取流, 参考 https://www.baeldung.com/spring-rest-template-interceptor
*/
@Bean
public RestTemplate restTemplate() {
HttpComponentsClientHttpRequestFactory rqstFactory = new HttpComponentsClientHttpRequestFactory();
rqstFactory.setConnectionRequestTimeout(1*1000); // 从pool池中获取可用连接的时间,1 s
rqstFactory.setConnectTimeout(10*1000); // 与远程服务器建立一个新连接所需要的时间, 10 s
rqstFactory.setReadTimeout(30*1000); // 数据包开始传输之前的等待时间, 30 s
RestTemplate template = new RestTemplate(
// 多次使用 getBody() response body
new BufferingClientHttpRequestFactory(rqstFactory));
List<ClientHttpRequestInterceptor> interceptors = template.getInterceptors();
if (CollectionUtils.isEmpty(template.getInterceptors())) {
interceptors = restInterceptor();
} else {
interceptors.addAll(restInterceptor()); // 无论调用几次该方法,总是获取的是容器中的相同的一个 单例Bean。这也是为什么 restInterceptor() 必须使用 @Bean 注解
}
template.setInterceptors(interceptors);
return template;
}
}
测试
不启动项目,直接测试 ,方式有3: 1) main 方法 内各种 new 对象之后 调用 ;2) @RunWith + @Autowired 注入Bean对象调用;3) 不使用 @RunWith + @Autowired 也能调用
1) 掠过
2) @RunWith + @Autowired 注入Bean对象调用
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.http.*;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.client.RestTemplate;
import java.util.HashMap;
import java.util.Map;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
@RunWith(SpringJUnit4ClassRunner.class) // 在该类中运行测试,提供一些 spring 的某些特性支持
@ContextConfiguration(classes = RestClientConfig.class) // 利用SpringJUnit4ClassRunner 特性注入 Bean
public class RestTemplateConfigTest {
@Autowired //利用SpringJUnit4ClassRunner特性,直接使用 @Autowired 注入
RestTemplate restTemplate;
@Test
public void test01() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON); // 指定传输参数的格式为 json
HttpEntity<Object> request = new HttpEntity<Object>(null, headers);
ResponseEntity<String> responseEntity = restTemplate.postForEntity("http://httpbin.org/post", request, String.class);
assertThat(responseEntity.getHeaders()
.get("Foo")
.get(0), is(equalTo("bar")));
System.out.println("大多时候,成功的解决了提出问题的人,就以为万事大吉。典型的 国产思维。这可不好呀,都是自己人。");
}
}
3) 不使用 @RunWith + @Autowired
public class RestTemplateConfigTest {
@Test
public void test02() {
ApplicationContext context = new AnnotationConfigApplicationContext(RestClientConfig.class);
RestTemplate restTemplate = context.getBean(RestTemplate.class);
String object = restTemplate.getForObject("https://不要解决提出问题的人.ok?param=yes", String.class);
}
}
补充几句:
@Runwith 的 作用 如下所示,更多 使用例子参考