RestTemplateUtil

1.添加httpclient依赖作为restTemplate的实现

  1. <dependency>
  2. <groupId>org.apache.httpcomponents</groupId>
  3. <artifactId>httpclient</artifactId>
  4. </dependency>

2.配置

  • 使用clientHttpRequestFactory, 可以设置超时时间, 连接池等等
  • 使用自定义的fastJson转换器
  • 使用自定义拦截器可以在每次发送请求时添加header等信息 ```java package com.bestser.factory.cloud;

import com.alibaba.fastjson.serializer.SerializerFeature; import com.alibaba.fastjson.support.config.FastJsonConfig; import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; import com.bestser.factory.config.security.oauth2.AuthorizationConstants; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.List; import org.apache.commons.lang3.StringUtils; import org.apache.http.client.HttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.client.RestTemplate;

/**

  • RestTemplate配置类 *
  • @author gavin
  • @date 2020-07-01
  • 文档: https://docs.spring.io/spring/docs/4.3.9.RELEASE/spring-framework-reference/html/remoting.html#rest-client-access
  • api: https://docs.spring.io/spring-framework/docs/4.3.9.RELEASE/javadoc-api/org/springframework/web/client/RestTemplate.html */ @Configuration public class RestTemplateConfig {

    private static final Logger LOGGER = LoggerFactory.getLogger(RestTemplateConfig.class); private StringRedisTemplate redisTemplate;

    public RestTemplateConfig(StringRedisTemplate redisTemplate) {

    1. this.redisTemplate = redisTemplate;

    }

    @Bean public RestTemplate restTemplate(ClientHttpRequestInterceptor interceptor) {

    1. RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory());
    2. restTemplate.setInterceptors(Collections.singletonList(interceptor));
    3. replaceJackson2FastJson(restTemplate);
    4. return restTemplate;

    }

    @Bean public RestTemplate authorizedRestTemplate(ClientHttpRequestInterceptor interceptor) {

    1. RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory());
    2. restTemplate.setInterceptors(Collections.singletonList(interceptor));
    3. replaceJackson2FastJson(restTemplate);
    4. return restTemplate;

    }

    /**

    • 替换默认的jackson转换器为fastJson转换器 */ private void replaceJackson2FastJson(RestTemplate restTemplate) { List> converters = restTemplate.getMessageConverters(); //原有的String是ISO-8859-1编码 替换成 UTF-8编码 converters.removeIf(c -> c instanceof StringHttpMessageConverter); converters.add(new StringHttpMessageConverter(StandardCharsets.UTF_8)); converters.add(0, fastJsonHttpMessageConverter()); }

      /**

    • 配置fastJson转换器 */ @Bean public HttpMessageConverter fastJsonHttpMessageConverter() { FastJsonConfig fastJsonConfig = new FastJsonConfig(); fastJsonConfig.setSerializerFeatures(SerializerFeature.WriteMapNullValue, SerializerFeature.QuoteFieldNames,

      1. SerializerFeature.WriteNullStringAsEmpty, SerializerFeature.WriteNullListAsEmpty,
      2. SerializerFeature.DisableCircularReferenceDetect);

      FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter(); fastJsonHttpMessageConverter.setSupportedMediaTypes(Collections.singletonList(MediaType.APPLICATION_JSON_UTF8)); fastJsonHttpMessageConverter.setFastJsonConfig(fastJsonConfig); return fastJsonHttpMessageConverter; }

      /**

    • 配置clientHttpRequestFactory */ @Bean public HttpComponentsClientHttpRequestFactory clientHttpRequestFactory() { try {

      1. HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
      2. //设置连接池
      3. PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
      4. //最大连接数
      5. connectionManager.setMaxTotal(20);
      6. //同路由并发数
      7. connectionManager.setDefaultMaxPerRoute(10);
      8. httpClientBuilder.setConnectionManager(connectionManager);
      9. HttpClient httpClient = httpClientBuilder.build();
      10. // httpClient连接配置
      11. HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(
      12. httpClient);
      13. //连接超时
      14. requestFactory.setConnectTimeout(60 * 1000);
      15. //数据读取超时时间
      16. requestFactory.setReadTimeout(60 * 1000);
      17. //连接不够用的等待时间
      18. requestFactory.setConnectionRequestTimeout(60 * 1000);
      19. return requestFactory;

      } catch (Exception e) {

      1. LOGGER.error(String.format("初始化clientHttpRequestFactory失败, 错误信息: %s", e));

      } return null; }

      /**

    • 自定义拦截器携带token和authorization */ @Bean public ClientHttpRequestInterceptor authorizedRequestInterceptor() { return (httpRequest, bytes, clientHttpRequestExecution) -> {
      1. HttpHeaders headers = httpRequest.getHeaders();
      2. String token = (String) SecurityContextHolder.getContext().getAuthentication().getCredentials();
      3. if (!StringUtils.isBlank(token)) {
      4. headers.add(AuthorizationConstants.TOKEN, token);
      5. }
      6. String authorization = redisTemplate.opsForValue()
      7. .get(AuthorizationConstants.OAUTH2_TOKEN_KEY_PREFIX + token);
      8. if (!StringUtils.isBlank(authorization)) {
      9. headers.add(AuthorizationConstants.AUTHORIZATION, authorization);
      10. }
      11. return clientHttpRequestExecution.execute(httpRequest, bytes);
      }; } }
  1. 3.工具类, http请求的封装
  2. ```java
  3. package com.bestser.factory.cloud;
  4. import com.alibaba.fastjson.JSONObject;
  5. import org.apache.commons.logging.Log;
  6. import org.apache.commons.logging.LogFactory;
  7. import org.springframework.http.HttpMethod;
  8. import org.springframework.stereotype.Component;
  9. import org.springframework.web.client.RestTemplate;
  10. /**
  11. * 发送http请求工具类
  12. *
  13. * @author gavin
  14. * @date 2020-07-04
  15. */
  16. @Component
  17. public class RestTemplateUtil {
  18. private final static Log LOGGER = LogFactory.getLog(RestTemplateUtil.class);
  19. private final static int SUCCESS_CODE = 200;
  20. private final static Object EMPTY_REQUEST_BODY = new Object();
  21. private static RestTemplate rest;
  22. public RestTemplateUtil(RestTemplate restTemplate) {
  23. rest = restTemplate;
  24. }
  25. @SuppressWarnings("unchecked")
  26. private static <T> CloudResponse<T> doCall(HttpMethod method, String url, Object requestBody) {
  27. CloudResponse<T> response = new CloudResponse<>();
  28. switch (method) {
  29. case GET:
  30. response = rest.getForObject(url, CloudResponse.class);
  31. break;
  32. case POST:
  33. response = rest.postForObject(url, requestBody, CloudResponse.class);
  34. break;
  35. case PUT:
  36. rest.put(url, requestBody);
  37. response.setState(SUCCESS_CODE);
  38. break;
  39. case DELETE:
  40. rest.delete(url);
  41. response.setState(SUCCESS_CODE);
  42. break;
  43. default:
  44. throw new RuntimeException(String.format("未知请求类型: %s", method));
  45. }
  46. return response;
  47. }
  48. /**
  49. * 发送http请求
  50. *
  51. * @param method 请求方法
  52. * @param url 请求url
  53. * @param requestBody 请求体
  54. * @param <T> 返回值的泛型
  55. * @return T
  56. */
  57. private static <T> T call(HttpMethod method, String url, Object requestBody) {
  58. CloudResponse<T> response;
  59. try {
  60. response = doCall(method, url, requestBody);
  61. LOGGER.info(String.format("url: %s, requestBody: %s, response: %s", url, JSONObject.toJSON(requestBody),
  62. JSONObject.toJSON(response)));
  63. } catch (Exception e) {
  64. LOGGER.error(
  65. String.format("url: %s, requestBody: %s, errorMessage: %s", url, JSONObject.toJSON(requestBody),
  66. e.getMessage()));
  67. throw e;
  68. }
  69. if (response.getState() != SUCCESS_CODE) {
  70. LOGGER.error(
  71. String.format("url: %s, requestBody: %s, errorMessage: %s", url, JSONObject.toJSON(requestBody),
  72. response.getMessage()));
  73. throw new RuntimeException(
  74. String.format("url: %s, requestBody: %s, errorMessage: %s", url, JSONObject.toJSON(requestBody),
  75. response.getMessage()));
  76. }
  77. return response.getData();
  78. }
  79. public static <T> T get(String url) {
  80. return call(HttpMethod.GET, url, EMPTY_REQUEST_BODY);
  81. }
  82. public static <T> T post(String url) {
  83. return call(HttpMethod.POST, url, EMPTY_REQUEST_BODY);
  84. }
  85. public static <T> T post(String url, Object requestBody) {
  86. return call(HttpMethod.POST, url, requestBody);
  87. }
  88. public static void put(String url) {
  89. call(HttpMethod.PUT, url, EMPTY_REQUEST_BODY);
  90. }
  91. public static void put(String url, Object requestBody) {
  92. call(HttpMethod.PUT, url, requestBody);
  93. }
  94. public static void delete(String url) {
  95. call(HttpMethod.DELETE, url, EMPTY_REQUEST_BODY);
  96. }
  97. public static void delete(String url, Object requestBody) {
  98. call(HttpMethod.DELETE, url, requestBody);
  99. }
  100. }

优化版本(添加自定义请求头参数):

  1. package com.bestser.factory.cloud;
  2. import com.alibaba.fastjson.JSONObject;
  3. import org.apache.commons.logging.Log;
  4. import org.apache.commons.logging.LogFactory;
  5. import org.springframework.http.HttpEntity;
  6. import org.springframework.http.HttpHeaders;
  7. import org.springframework.http.HttpMethod;
  8. import org.springframework.http.MediaType;
  9. import org.springframework.stereotype.Component;
  10. import org.springframework.web.client.RestTemplate;
  11. /**
  12. * 发送http请求工具类
  13. *
  14. * @author gavin
  15. * @date 2020-07-04
  16. */
  17. @Component
  18. public class RestTemplateUtil {
  19. private final static Log LOGGER = LogFactory.getLog(RestTemplateUtil.class);
  20. private final static int SUCCESS_CODE = 200;
  21. private final static Object EMPTY_REQUEST_BODY = new Object();
  22. private final static HttpHeaders EMPTY_REQUEST_HEADER = new HttpHeaders();
  23. private static RestTemplate rest;
  24. public RestTemplateUtil(RestTemplate restTemplate) {
  25. rest = restTemplate;
  26. }
  27. /**
  28. * 发送http请求
  29. *
  30. * @param method 请求方法
  31. * @param url 请求url
  32. * @param requestBody 请求体
  33. * @param <T> 返回值的泛型
  34. * @return T
  35. */
  36. @SuppressWarnings("unchecked")
  37. private static <T> T call(String url, HttpMethod method, HttpHeaders requestHeaders, Object requestBody) {
  38. CloudResponse<T> response;
  39. requestHeaders.setContentType(MediaType.APPLICATION_JSON);
  40. HttpEntity<Object> requestEntity = new HttpEntity<>(requestBody, requestHeaders);
  41. try {
  42. response = rest.exchange(url, method, requestEntity, CloudResponse.class).getBody();
  43. LOGGER.info(String.format("url: %s, requestBody: %s, response: %s", url, JSONObject.toJSON(requestBody),
  44. JSONObject.toJSON(response)));
  45. } catch (Exception e) {
  46. LOGGER.error(
  47. String.format("url: %s, requestBody: %s, errorMessage: %s", url, JSONObject.toJSON(requestBody),
  48. e.getMessage()));
  49. throw e;
  50. }
  51. if (response == null || response.getState() != SUCCESS_CODE) {
  52. LOGGER.error(
  53. String.format("url: %s, requestBody: %s, errorMessage: %s", url, JSONObject.toJSON(requestBody),
  54. response == null ? "null" : response.getMessage()));
  55. throw new RuntimeException(
  56. String.format("url: %s, requestBody: %s, errorMessage: %s", url, JSONObject.toJSON(requestBody),
  57. response == null ? "null" : response.getMessage()));
  58. }
  59. return response.getData();
  60. }
  61. public static <T> T get(String url) {
  62. return call(url, HttpMethod.GET, EMPTY_REQUEST_HEADER, EMPTY_REQUEST_BODY);
  63. }
  64. public static <T> T get(String url, HttpHeaders httpHeaders) {
  65. return call(url, HttpMethod.GET, httpHeaders, EMPTY_REQUEST_BODY);
  66. }
  67. public static <T> T post(String url) {
  68. return call(url, HttpMethod.POST, EMPTY_REQUEST_HEADER, EMPTY_REQUEST_BODY);
  69. }
  70. public static <T> T post(String url, Object requestBody) {
  71. return call(url, HttpMethod.POST, EMPTY_REQUEST_HEADER, requestBody);
  72. }
  73. public static void put(String url) {
  74. call(url, HttpMethod.PUT, EMPTY_REQUEST_HEADER, EMPTY_REQUEST_BODY);
  75. }
  76. public static void put(String url, Object requestBody) {
  77. call(url, HttpMethod.PUT, EMPTY_REQUEST_HEADER, requestBody);
  78. }
  79. public static void delete(String url) {
  80. call(url, HttpMethod.DELETE, EMPTY_REQUEST_HEADER, EMPTY_REQUEST_BODY);
  81. }
  82. }

4.标准返回类

  1. package com.bestser.factory.cloud;
  2. import lombok.Data;
  3. /**
  4. * @author gavin
  5. * @date 2020-07-04
  6. */
  7. @Data
  8. public class CloudResponse<T> {
  9. private Integer state;
  10. private String message;
  11. private T data;
  12. }

CloudUrlBuilder

1.url构建工具类

  1. package com.bestser.factory.cloud;
  2. import java.util.Collections;
  3. import java.util.Map;
  4. import org.springframework.stereotype.Component;
  5. import org.springframework.util.CollectionUtils;
  6. import org.springframework.web.util.UriComponents;
  7. import org.springframework.web.util.UriComponentsBuilder;
  8. /**
  9. * cloud端url构建器
  10. *
  11. * @author gavin
  12. * @date 2020-07-03
  13. */
  14. @Component
  15. public class CloudUrlBuilder {
  16. private static final String HTTP = "http";
  17. private static final String AUTH_HOST = "localhost:8612";
  18. private static final String INVENTORY_HOST = "localhost:8610";
  19. /**
  20. * 构建url
  21. *
  22. * @param host 主机ip:端口
  23. * @param path 路径
  24. * @param params 请求参数
  25. * @param args 路径参数
  26. * @return url
  27. */
  28. private static String buildUrl(String host, CloudResource path, Map<String, Object> params, String... args) {
  29. UriComponentsBuilder builder = UriComponentsBuilder.newInstance().scheme(HTTP).host(host)
  30. .path(path.getPath());
  31. if (!CollectionUtils.isEmpty(params)) {
  32. params.forEach(builder::queryParam);
  33. }
  34. UriComponents build = builder.build();
  35. if (args != null && args.length > 0) {
  36. return build.expand(args).toUriString();
  37. }
  38. return build.toUriString();
  39. }
  40. public static String buildAuthUrl(CloudResource path, String... args) {
  41. return buildAuthUrl(path, Collections.emptyMap(), args);
  42. }
  43. public static String buildAuthUrl(CloudResource path, Map<String, Object> params) {
  44. return buildAuthUrl(path, params, null);
  45. }
  46. public static String buildAuthUrl(CloudResource path, Map<String, Object> params, String... args) {
  47. return buildUrl(AUTH_HOST, path, params, args);
  48. }
  49. public static String buildInventoryUrl(CloudResource path, String... args) {
  50. return buildInventoryUrl(path, Collections.emptyMap(), args);
  51. }
  52. public static String buildInventoryUrl(CloudResource path, Map<String, Object> params) {
  53. return buildInventoryUrl(path, params, null);
  54. }
  55. public static String buildInventoryUrl(CloudResource path, Map<String, Object> params, String... args) {
  56. return buildUrl(INVENTORY_HOST, path, params, args);
  57. }
  58. }

2.url资源路径的定义

  1. package com.bestser.factory.cloud;
  2. /**
  3. * cloud端资源路径
  4. *
  5. * @author gavin
  6. * @date 2020-07-01
  7. */
  8. public enum CloudResource {
  9. /**
  10. * oauth2令牌
  11. */
  12. OAUTH2_TOKEN("oauth2令牌", "/oauth/token"),
  13. /**
  14. * 客户端权限
  15. */
  16. CLIENT_PRIVILEGE("客户端权限", "/auth/{serviceNames}"),
  17. /**
  18. * 配件
  19. */
  20. PART("配件", "/product/part"),
  21. PART_VALUE("配件值", "/product/part/{propertyValueId}"),
  22. PART_LIST("配件列表", "/product/part/list");
  23. private final String caption;
  24. private final String path;
  25. CloudResource(String caption, String path) {
  26. this.caption = caption;
  27. this.path = path;
  28. }
  29. public String caption() {
  30. return this.caption;
  31. }
  32. public String getPath() {
  33. return path;
  34. }
  35. }

使用示例

调用方

  1. @RestController
  2. @ResponseResult
  3. @RequestMapping("/api/setting/part")
  4. public class PartSettingController {
  5. @Autowired
  6. private DesignPartService designPartService;
  7. @GetMapping("/list")
  8. public List<ProductPropertyVO> listParts() {
  9. return RestTemplateUtil.get(CloudUrlBuilder.buildInventoryUrl(CloudResource.PART_LIST));
  10. }
  11. @PostMapping
  12. public void createParts(@RequestBody ProductPropertyVO vo) {
  13. if (StringUtils.isBlank(vo.getPropertyValue())) {
  14. throw new RestMessageException(ResponseStateEnum.PARAM_ERROR, "配件名称不能为空");
  15. }
  16. RestTemplateUtil.post(CloudUrlBuilder.buildInventoryUrl(CloudResource.PART), vo);
  17. }
  18. @PutMapping
  19. @Transactional(rollbackFor = Exception.class)
  20. public void updatePart(@RequestBody ProductPropertyVO vo) {
  21. if (StringUtils.isBlank(vo.getPropertyValue()) || StringUtils.isBlank(vo.getNewPropertyValue())) {
  22. throw new RestMessageException(ResponseStateEnum.PARAM_ERROR, "配件名称不能为空");
  23. }
  24. // 修改 design_part 表的配件名称
  25. designPartService.updatePartName(vo.getPropertyValue(), vo.getNewPropertyValue());
  26. RestTemplateUtil.put(CloudUrlBuilder.buildInventoryUrl(CloudResource.PART), vo);
  27. }
  28. @DeleteMapping
  29. @Transactional(rollbackFor = Exception.class)
  30. public void deletePart(@RequestBody ProductPropertyVO vo) {
  31. if (designPartService.isPartUsed(vo.getPropertyValue())) {
  32. throw new RestMessageException(ResponseStateEnum.PARAM_ERROR, "配件已被使用, 不能删除");
  33. }
  34. RestTemplateUtil.delete(CloudUrlBuilder
  35. .buildInventoryUrl(CloudResource.PART_VALUE, String.valueOf(vo.getPropertyValueId())));
  36. RestTemplateUtil.delete(CloudUrlBuilder
  37. .buildInventoryUrl(CloudResource.PART_VALUE, String.valueOf(vo.getPropertyValueId())));
  38. }
  39. }

被调用方

  1. @ApiOperation("获取配件列表")
  2. @GetMapping("/part/list")
  3. public List<PropertyValueVO> listParts() {
  4. return productService.listPropertiesByName("配件名称");
  5. }
  6. @ApiOperation("新增配件, 参数中propertyValue必填")
  7. @PostMapping("/part")
  8. public void createParts(@RequestBody ProductPropertyParam param) {
  9. if (StringUtils.isBlank(param.getPropertyValue())) {
  10. throw new ServiceException("配件名称不能为空");
  11. }
  12. productService.createPart(param.getPropertyValue());
  13. }
  14. @ApiOperation("修改配件名称, 参数中propertyValueId和propertyValue必填")
  15. @PutMapping("/part")
  16. public void updatePart(@RequestBody ProductPropertyParam param) {
  17. if (StringUtils.isBlank(param.getPropertyValue())) {
  18. throw new ServiceException("配件名称不能为空");
  19. }
  20. productService.updateProductPropertyValue(param.getPropertyValueId(), param.getNewPropertyValue());
  21. }
  22. @ApiOperation("删除配件, 参数中propertyValueId必填")
  23. @DeleteMapping("/part/{propertyValueId}")
  24. public void deletePart(@PathVariable("propertyValueId") Integer propertyValueId) {
  25. productService.deleteProductPropertyValue(propertyValueId);
  26. }