Java中調(diào)用第三方接口的詳細代碼示例
0、測試接口
寫兩個測試接口,一個GET,一個POST
@RestController
@RequestMapping("/svc1")
public class Controller {
@GetMapping("/t1")
public String doGet(@RequestParam(required = false) String name) {
return "test" + name;
}
@PostMapping("/t2")
public ResultVo doPost(@RequestBody RequestBodyDto dto, @RequestParam String key) {
return new ResultVo(200, "操作成功", dto.getName() + dto.getChoose() + key);
}
}
1、JDK的HttpURLConnection
原生版,主要依靠JDK的 java.net包,GET請求:
import java.net.HttpURLConnection;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
@Slf4j
public class TestDemo {
public static void main(String[] args) {
BufferedReader reader = null;
try {
// 創(chuàng)建URL對象
URL url = new URL("http://localhost:8080/svc1/t1");
// 打開連接
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
// 讀取響應(yīng)
reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
// 處理響應(yīng)
String inputLine;
StringBuilder response = new StringBuilder();
while ((inputLine = reader.readLine()) != null) {
response.append(inputLine);
}
System.out.println(response);
} catch (Exception e) {
log.error("調(diào)用失敗");
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
URL類是JDK java.net包下的一個類,表示一個統(tǒng)一資源標識符(Uniform Resource Identifier)引用
POST請求:
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import com.alibaba.fastjson.JSON;
@Slf4j
public class TestDemo {
public static void main(String[] args) {
try {
// 創(chuàng)建URL對象
URL url = new URL("http://localhost:8080/svc1/t2?key=abc");
// 打開連接
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
// 設(shè)置請求頭與數(shù)據(jù)格式
connection.setRequestProperty("Content-Type", "application/json; utf-8");
connection.setRequestProperty("Accept", "application/json");
// 允許向服務(wù)器寫入數(shù)據(jù)
connection.setDoOutput(true);
RequestBodyDto dto = new RequestBodyDto("Tom", "A");
String json = JSON.toJSONString(dto);
// 寫入JSON到請求體
try (OutputStream os = connection.getOutputStream()) {
BufferedOutputStream bos = new BufferedOutputStream(os);
bos.write(json.getBytes(StandardCharsets.UTF_8));
bos.flush();
}
// 讀取響應(yīng)
try (BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"))) {
StringBuilder response = new StringBuilder();
String responseLine;
while ((responseLine = br.readLine()) != null) {
response.append(responseLine.trim());
}
System.out.println("Response: " + response.toString());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

2、Apache的HttpClient
后續(xù)這些方式,本質(zhì)上就是對java.net包的一個封裝了。先引入Apache做http請求的依賴坐標:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4.16</version>
</dependency>public class TestDemo {
public static void main(String[] args) {
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
// 創(chuàng)建POST請求對象
HttpPost httpPost = new HttpPost("http://localhost:8080/svc1/t2?key=abc");
// 設(shè)置請求頭
httpPost.setHeader("Content-Type", "application/json; utf-8");
httpPost.setHeader("Accept", "application/json");
// 設(shè)置請求體
RequestBodyDto dto = new RequestBodyDto("Tom", "A");
String json = JSON.toJSONString(dto);
StringEntity entity = new StringEntity(json);
httpPost.setEntity(entity);
// 執(zhí)行請求并獲取響應(yīng)
CloseableHttpResponse response = httpClient.execute(httpPost);
HttpEntity responseEntity = response.getEntity();
// 處理響應(yīng)
if (null != responseEntity) {
String responseStr = EntityUtils.toString(responseEntity);
System.out.println(responseStr);
// 也可按需把json串反序列化成Java對象,略
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
3、SpringBoot的RestTemplate
使用SpringBoot封裝的RestTemplate,依賴寫web的:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>把RestTemplate的Bean放到IoC容器中:
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
3.1 GET
發(fā)送Get請求,常用方法:
- getForObject
- getForEntity
/** * url為請求的地址 * responseType為請求響應(yīng)body的類型 * urlVariables為url中的參數(shù)綁定 * */ getForEntity(Stringurl,Class responseType,Object…urlVariables) /** * URI對象來替代之前getForEntity的url和urlVariables參數(shù)來指定訪問地址和參數(shù)綁定 * URI是JDK java.net包下的一個類 * */ getForEntity(URI url,Class responseType)
示例:
@SpringBootTest
class LearningApplicationTests {
@Resource
private RestTemplate restTemplate;
@Test
void contextLoads() {
String url = "http://localhost:8080/svc1/t1?name={name}";
// 參數(shù)
Map<String, String> paramMap = new HashMap<>();
paramMap.put("name", "Tom");
ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class, paramMap);
// 狀態(tài)碼
HttpStatus statusCode = responseEntity.getStatusCode();
// 響應(yīng)
String body = responseEntity.getBody();
System.out.println(statusCode + body);
}
}接口路徑不用字符串,改為URI對象:
@Test
void testTemplate() {
String url = "http://localhost:8080/svc1/t1";
String name = "Tom";
// 使用 UriComponentsBuilder 構(gòu)建 URL
URI uri = UriComponentsBuilder.fromHttpUrl(url)
.queryParam("name", name)
.build()
.toUri();
ResponseEntity<String> responseEntity = restTemplate.getForEntity(uri, String.class);
// 狀態(tài)碼
HttpStatus statusCode = responseEntity.getStatusCode();
// 響應(yīng)
String body = responseEntity.getBody();
System.out.println(statusCode + body);
}
最后,getForObject:
getForObject(String url,Class responseType,Object...urlVariables) getForObject(String url,Class responseType,Map urlVariables) getForObject(URI url,Class responseType)
和getForEntity的區(qū)別是,getForObject只有一個響應(yīng)的內(nèi)容,響應(yīng)碼、響應(yīng)頭等沒有

3.2 POST
常用方法:
- postForEntity
- postForObject
- postForLocation
以postForEntity為例,其參數(shù)可選:(重載)
postForEntity(String url,Object request,Class responseType,Object... uriVariables) postForEntity(String url,Object request,Class responseType,Map uriVariables) postForEntity(URI url,Object request,Class responseType)
示例:
@Test
void testTemplate2() {
String url = "http://localhost:8080/svc1/t2?key=Tom";
RestTemplate restTemplate = new RestTemplate();
// 請求頭
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.AUTHORIZATION, "Bear xx");
// headers.set("Content-Type", "application/x-www-form-urlencoded");
headers.add(HttpHeaders.CONTENT_TYPE, "application/json");
// 創(chuàng)建請求體對象并放入數(shù)據(jù)
HttpEntity<RequestBodyDto> requestData = new HttpEntity<>(new RequestBodyDto("Tom", "A"), headers);
// 和postForEntity一個意思
ResponseEntity<String> responseEntity = restTemplate.exchange(
url,
HttpMethod.POST,
requestData,
String.class
);
// 獲取響應(yīng)狀態(tài)碼和響應(yīng)體
HttpStatus statusCode = responseEntity.getStatusCode();
String responseBody = responseEntity.getBody();
System.out.println(statusCode + " " + responseBody);
}
4、SpringCloud的Feign
上面的RestTemplate,在調(diào)三方接口時挺好用的,但微服務(wù)架構(gòu)下,各個微服務(wù)之間調(diào)用時,url就不好寫,由此,用Feign:一個聲明式的http客戶端
核心思路是聲明出:
- 你調(diào)誰
- 用什么方式
- 請求參數(shù)是啥
- 返回類型是啥
引入依賴:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
啟動類上加上@EnableFeignClients
//在order的啟動類中開啟Feign
@EnableFeignClients
@MapperScan("com.llg.order.mapper")
@SpringBootApplication
public class OrderApplication{
public static void main(String[] args){
SpringApplication.run(OrderApplication.class,args);
}
}- 以order服務(wù)調(diào)用user服務(wù)為例,編寫調(diào)用方:
// 遠程調(diào)用userservice服務(wù)
@FeignClient("userservice")
public interface UserClient {
@GetMapping("/user/{id}")
User findById(@PathVariable("id") Long id);
// 后續(xù)接口自行添加
}
!!findById這個方法名隨便起
!!調(diào)用的接口路徑、調(diào)用的服務(wù)名、請求參數(shù)、返回類型聲明正確就行主要是基于SpringMVC的注解來聲明遠程調(diào)用的信息,比如:
?服務(wù)名稱:userservice
?請求方式:GET
?請求路徑:/user/{id}
?請求參數(shù):Long id
?返回值類型:User
- 注入上面定義的FeignClient類,也就是UserClient,直接調(diào)用聲明的那個方法
@Autowired
private UserClient userClient;
public Order queryOrderById(Long orderId){
//查詢訂單
Order order = orderMapper.findById(orderId);
//利用feign發(fā)起http請求,查用戶
User user = userClient.findById(order.getUserId());
//封裝,對象的某個屬性也是個對象,即引用類型
order.setUser(user);
return order;
} - 被調(diào)用方有多個實例時,負載均衡也不用考慮,F(xiàn)eign用了Ribbon做負載均衡
- 關(guān)于Feign請求頭的添加,可重寫RequestInterceptor的apply方法:
@Configuration
public class FeignConfig implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
//添加token
requestTemplate.header("token", "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJ4ZGFwYXBwaWQiOiIzNDgxMjU4ODk2OTI2OTY1NzYiLCJleHAiOjE2NjEyMjY5MDgsImlhdCI6MTY2MTIxOTcwOCwieGRhcHRlbmFudGlkIjoiMzAwOTgxNjA1MTE0MDUyNjA5IiwieGRhcHVzZXJpZCI6IjEwMDM0NzY2MzU4MzM1OTc5NTIwMCJ9.fZAO4kJSv2rSH0RBiL1zghdko8Npmu_9ufo6Wex_TI2q9gsiLp7XaW7U9Cu7uewEOaX4DTdpbFmMPvLUtcj_sQ");
}
}
- 要做降級邏輯的話:如下,調(diào)用消息中心服務(wù)
// @FeignClient的fallbackFactory指定下降級邏輯的類
@Component
@FeignClient(contextId = "remoteMessageService", value = ServiceNameConstants.MESSAGE_SERVICE, fallbackFactory = RemoteMessageFallbackFactory.class)
public interface RemoteMessageService {
/**
* 發(fā)送定時消息任務(wù):每分鐘掃描發(fā)送消息
*
* @return 結(jié)果
*/
@GetMapping("/inner/message/sendTimingMessage")
public R<Void> sendTimingMessage();
/**
* 發(fā)送系統(tǒng)消息
*
* @return 結(jié)果
*/
@PostMapping("/inner/message/sendSystemMessage")
public R<Void> sendSystemMessage(@RequestBody MessageSendSystemDto messageSendSystemDto);
}
// 降級邏輯
@Component
public class RemoteMessageFallbackFactory implements FallbackFactory<RemoteMessageService>{
private static final Logger log = LoggerFactory.getLogger(RemoteMessageFallbackFactory.class);
@Override
public RemoteMessageService create(Throwable throwable) {
throwable.printStackTrace();
log.error("消息服務(wù)調(diào)用失敗:{}", throwable.getMessage());
return new RemoteMessageService() {
@Override
public R<Void> sendTimingMessage() {
return R.fail("調(diào)用發(fā)送定時消息接口失敗:" + throwable.getMessage());
}
@Override
public R<Void> sendSystemMessage(MessageSendSystemDto messageSendSystemDto) {
return R.fail("調(diào)用發(fā)送消息接口失敗:" + throwable.getMessage());
}
};
}
}
5、Hutool的HttpUtil
還是對 java.net的封裝,引入依賴:
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.16</version> <!-- 請檢查最新版本 -->
</dependency>處理GET和POST:
/**
* @param url baseUrl
* @param requestMethod 請求方式
* @param headerMap 請求頭參數(shù)key-value
* @param paramMap 路徑參數(shù)key-value,形如?name=Tom&country=Chain
* @param bodyJsonStr post的body傳參,json字符串
* @return 響應(yīng)體
*/
public static String sendRequest(String url, Method requestMethod, Map<String, String> headerMap, Map<String, Object> paramMap, String bodyJsonStr) {
// 路徑參數(shù)不為空時,拼接URL
if (paramMap != null) {
UrlBuilder urlBuilder = UrlBuilder.of(url);
paramMap.forEach((k, v) -> urlBuilder.addQuery(k, v));
url = urlBuilder.toString();
}
//發(fā)送請求
HttpResponse httpResponse = HttpUtil.createRequest(requestMethod, url)
.addHeaders(headerMap)
.body(bodyJsonStr)
.execute();
return httpResponse.body();
}
測試下:
@Test
void testHuTool() {
String url = "http://localhost:8080/svc1/t1";
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("name", "Tom");
Map<String, String> headerMap = new HashMap<>();
headerMap.put("Authorization", "Bear xx");
String response = sendRequest(url, Method.GET, headerMap, paramMap, null);
System.out.println(response);
}
@Test
void testHuTool2() {
String url = "http://localhost:8080/svc1/t2";
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("key", "Tom");
Map<String, String> headerMap = new HashMap<>();
headerMap.put("Authorization", "Bear xx");
RequestBodyDto dto = new RequestBodyDto("Tom", "A");
String bodyJsonStr = JSON.toJSONString(dto);
String response = sendRequest(url, Method.POST, headerMap, paramMap, bodyJsonStr);
System.out.println(response);
}
6、失敗后重試
考慮到遠程調(diào)用可能失敗,失敗后重試三次,以上面的hutool為例來實現(xiàn),其余的都一樣,主要還是一個是否成功標記位 + 一個計數(shù),successFlag不用voilate,并發(fā)安全也不用考慮,線程內(nèi)部調(diào)用的,用到的數(shù)存棧里了都。
/**
* @param url baseUrl
* @param requestMethod 請求方式
* @param headerMap 請求頭參數(shù)key-value
* @param paramMap 路徑參數(shù)key-value,形如?name=Tom&country=Chain
* @param bodyJsonStr post的body傳參,json字符串
* @return 響應(yīng)體
*/
public static String sendRequest(String url, Method requestMethod, Map<String, String> headerMap, Map<String, Object> paramMap, String bodyJsonStr) {
// 是否成功標記位
boolean successFlag = false;
// 重試次數(shù)累計
int retryCount = 1;
HttpResponse httpResponse = null;
while (!successFlag && retryCount <= 3) {
try {
// 路徑參數(shù)不為空時,拼接URL
if (paramMap != null) {
UrlBuilder urlBuilder = UrlBuilder.of(url);
paramMap.forEach((k, v) -> urlBuilder.addQuery(k, v));
url = urlBuilder.toString();
}
// 發(fā)送請求
httpResponse = HttpUtil.createRequest(requestMethod, url)
.addHeaders(headerMap)
.body(bodyJsonStr)
.execute();
if (httpResponse.getStatus() != 200) {
retryCount++;
} else {
successFlag = true;
}
} catch (Exception e) {
e.printStackTrace();
retryCount++;
}
}
return httpResponse == null ? null : httpResponse.body();
}總結(jié)
到此這篇關(guān)于Java中調(diào)用第三方接口的文章就介紹到這了,更多相關(guān)Java調(diào)用第三方接口內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Mybatis之foreach標簽內(nèi)傳入list為空的問題
這篇文章主要介紹了Mybatis之foreach標簽內(nèi)傳入list為空的問題及解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-03-03
java ThreadPoolExecutor使用方法簡單介紹
這篇文章主要介紹了java ThreadPoolExecutor使用方法簡單介紹的相關(guān)資料,需要的朋友可以參考下2017-02-02
Java中的break和continue關(guān)鍵字的使用方法總結(jié)
下面小編就為大家?guī)硪黄狫ava中的break和continue關(guān)鍵字的使用方法總結(jié)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-11-11
Redis原子計數(shù)器incr,防止并發(fā)請求操作
這篇文章主要介紹了Redis原子計數(shù)器incr,防止并發(fā)請求操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-11-11
Spring Boot應(yīng)用監(jiān)控的實戰(zhàn)教程
Spring Boot 提供運行時的應(yīng)用監(jiān)控和管理功能,下面這篇文章主要給大家介紹了關(guān)于Spring Boot應(yīng)用監(jiān)控的相關(guān)資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-05-05

