詳解如何使用Jersey客戶端請求Spring Boot(RESTFul)服務(wù)
本文介紹了使用Jersey客戶端請求Spring Boot(RESTFul)服務(wù),分享給大家,具體如下:
Jersey客戶端獲取Client對象實例封裝:
@Service("jerseyPoolingClient")
public class JerseyPoolingClientFactoryBean implements FactoryBean<Client>, InitializingBean, DisposableBean{
/**
* Client接口是REST客戶端的基本接口,用于和REST服務(wù)器通信。Client被定義為一個重量級的對象,其內(nèi)部管理著
* 客戶端通信底層的各種對象,比如連接器,解析器等。因此,不推薦在應(yīng)用中產(chǎn)生大量的的Client實例,這一點在開發(fā)中
* 需要特別小心,另外該接口要求其實例要有關(guān)閉連接的保障,否則會造成內(nèi)存泄露
*/
private Client client;
/**
* 一個Client最大的連接數(shù),默認為2000
*/
private int maxTotal = 2000;
/**
* 每路由的默認最大連接數(shù)
*/
private int defaultMaxPerRoute = 1000;
private ClientConfig clientConfig;
public JerseyPoolingClientFactoryBean() {
}
/**
* 帶配置的構(gòu)造函數(shù)
* @param clientConfig
*/
public JerseyPoolingClientFactoryBean(ClientConfig clientConfig) {
this.clientConfig = clientConfig;
}
public JerseyPoolingClientFactoryBean(int maxTotal, int defaultMaxPerRoute) {
this.maxTotal = maxTotal;
this.defaultMaxPerRoute = defaultMaxPerRoute;
}
/**
* attention:
* Details:容器銷毀時,釋放Client資源
* @author chhliu
*/
@Override
public void destroy() throws Exception {
this.client.close();
}
/**
*
* attention:
* Details:以連接池的形式,來初始化Client對象
* @author chhliu
*/
@Override
public void afterPropertiesSet() throws Exception {
// 如果沒有使用帶ClientConfig的構(gòu)造函數(shù),則該類的實例為null,則使用默認的配置初始化
if(this.clientConfig == null){
final ClientConfig clientConfig = new ClientConfig();
// 連接池管理實例,該類是線程安全的,支持多并發(fā)操作
PoolingHttpClientConnectionManager pcm = new PoolingHttpClientConnectionManager();
pcm.setMaxTotal(this.maxTotal);
pcm.setDefaultMaxPerRoute(this.defaultMaxPerRoute);
clientConfig.property(ApacheClientProperties.CONNECTION_MANAGER, pcm);
/*
* 在使用Jersey來請求Spring Boot服務(wù)時,Spring Boot默認使用Jackson來解析JSON
* 而Jersey默認使用MOXy解析JSON,當Jersey Client想Spring Boot服務(wù)請求資源時,
* 這個差異會導致服務(wù)端和客戶端對POJO的轉(zhuǎn)換不同,造成反序列化的錯誤
* 因此,此處需要在Client的Config實例中注冊Jackson特性
*/
clientConfig.register(JacksonFeature.class);
// 使用配置Apache連接器,默認連接器為HttpUrlConnector
clientConfig.connectorProvider(new ApacheConnectorProvider());
client = ClientBuilder.newClient(clientConfig);
}else{
// 使用構(gòu)造函數(shù)中的ClientConfig來初始化Client對象
client = ClientBuilder.newClient(this.clientConfig);
}
}
/**
* attention:
* Details:返回Client對象,如果該對象為null,則創(chuàng)建一個默認的Client
* @author chhliu
*/
@Override
public Client getObject() throws Exception {
if(null == this.client){
return ClientBuilder.newClient();
}
return this.client;
}
/**
* attention:
* Details:獲取Client對象的類型
* @author chhliu
*/
@Override
public Class<?> getObjectType() {
return (this.client == null ? Client.class : this.client.getClass());
}
/**
* attention:
* Details:Client對象是否為單例,默認為單例
* @author chhliu
*/
@Override
public boolean isSingleton() {
return true;
}
}
請求Spring Boot服務(wù)的封裝:
@Component("jerseyClient")
public class JerseyClient {
@Resource(name="jerseyPoolingClient")
private Client client;
/**
* attention:
* Details:通過id來查詢對象
* @author chhliu
*/
public ResultMsg<GitHubEntity> getResponseById(final String id) throws JsonProcessingException, IOException{
WebTarget webTarget = client.target("http://localhost:8080").path("/github/get/user/"+id);
Invocation.Builder invocationBuilder = webTarget.request(MediaType.APPLICATION_JSON);
GenericType<ResultMsg<GitHubEntity>> genericType = new GenericType<ResultMsg<GitHubEntity>>(){};
Response response = invocationBuilder.get();
if(response.getStatus() == 200){
/*
* 當調(diào)用readEntity方法時,程序會自動的釋放連接
* 即使沒有調(diào)用readEntity方法,直接返回泛型類型的對象,底層仍然會釋放連接
*/
return response.readEntity(genericType);
}else{
ResultMsg<GitHubEntity> res = new ResultMsg<GitHubEntity>();
res.setErrorCode(String.valueOf(response.getStatus()));
res.setErrorMsg(response.getStatusInfo().toString());
res.setOK(false);
return res;
}
}
/**
* attention:
* Details:分頁查詢
* @author chhliu
*/
public ResultMsg<Pager<GitHubEntity>> getGithubWithPager(final Integer pageOffset, final Integer pageSize, final String orderColumn){
WebTarget webTarget = client.target("http://localhost:8080").path("/github/get/users/page")
.queryParam("pageOffset", pageOffset)
.queryParam("pageSize", pageSize)
.queryParam("orderColumn", orderColumn);
// 注意,如果此處的媒體類型為MediaType.APPLICATION_JSON,那么對應(yīng)的服務(wù)中的參數(shù)前需加上@RequestBody
Invocation.Builder invocationBuilder = webTarget.request(MediaType.APPLICATION_JSON);
GenericType<ResultMsg<Pager<GitHubEntity>>> genericType = new GenericType<ResultMsg<Pager<GitHubEntity>>>(){};
Response response = invocationBuilder.get();
if(response.getStatus() == 200){
return response.readEntity(genericType);
}else{
ResultMsg<Pager<GitHubEntity>> res = new ResultMsg<Pager<GitHubEntity>>();
res.setErrorCode(String.valueOf(response.getStatus()));
res.setErrorMsg(response.getStatusInfo().toString());
res.setOK(false);
return res;
}
}
/**
* attention:
* Details:根據(jù)用戶名來查詢
* @author chhliu
*/
public ResultMsg<List<GitHubEntity>> getResponseByUsername(final String username) throws JsonProcessingException, IOException{
WebTarget webTarget = client.target("http://localhost:8080").path("/github/get/users/"+username);
Invocation.Builder invocationBuilder = webTarget.request(MediaType.APPLICATION_JSON);
GenericType<ResultMsg<List<GitHubEntity>>> genericType = new GenericType<ResultMsg<List<GitHubEntity>>>(){};
Response response = invocationBuilder.get();
if(response.getStatus() == 200){
return response.readEntity(genericType);
}else{
ResultMsg<List<GitHubEntity>> res = new ResultMsg<List<GitHubEntity>>();
res.setErrorCode(String.valueOf(response.getStatus()));
res.setErrorMsg(response.getStatusInfo().toString());
res.setOK(false);
return res;
}
}
/**
* attention:
* Details:根據(jù)id來刪除一個記錄
* @author chhliu
*/
public ResultMsg<GitHubEntity> deleteById(final String id) throws JsonProcessingException, IOException{
WebTarget target = client.target("http://localhost:8080").path("/github/delete/"+id);
GenericType<ResultMsg<GitHubEntity>> genericType = new GenericType<ResultMsg<GitHubEntity>>(){};
Response response = target.request().delete();
if(response.getStatus() == 200){
return response.readEntity(genericType);
}else{
ResultMsg<GitHubEntity> res = new ResultMsg<GitHubEntity>();
res.setErrorCode(String.valueOf(response.getStatus()));
res.setErrorMsg(response.getStatusInfo().toString());
res.setOK(false);
return res;
}
}
/**
* attention:
* Details:更新一條記錄
* @author chhliu
*/
public ResultMsg<GitHubEntity> update(final GitHubEntity entity) throws JsonProcessingException, IOException{
WebTarget target = client.target("http://localhost:8080").path("/github/put");
GenericType<ResultMsg<GitHubEntity>> genericType = new GenericType<ResultMsg<GitHubEntity>>(){};
Response response = target.request().buildPut(Entity.entity(entity, MediaType.APPLICATION_JSON)).invoke();
if(response.getStatus() == 200){
return response.readEntity(genericType);
}else{
ResultMsg<GitHubEntity> res = new ResultMsg<GitHubEntity>();
res.setErrorCode(String.valueOf(response.getStatus()));
res.setErrorMsg(response.getStatusInfo().toString());
res.setOK(false);
return res;
}
}
/**
* attention:
* Details:插入一條記錄
* @author chhliu
*/
public ResultMsg<GitHubEntity> save(final GitHubEntity entity) throws JsonProcessingException, IOException{
WebTarget target = client.target("http://localhost:8080").path("/github/post");
GenericType<ResultMsg<GitHubEntity>> genericType = new GenericType<ResultMsg<GitHubEntity>>(){};
Response response = target.request().buildPost(Entity.entity(entity, MediaType.APPLICATION_JSON)).invoke();
if(response.getStatus() == 200){
return response.readEntity(genericType);
}else{
ResultMsg<GitHubEntity> res = new ResultMsg<GitHubEntity>();
res.setErrorCode(String.valueOf(response.getStatus()));
res.setErrorMsg(response.getStatusInfo().toString());
res.setOK(false);
return res;
}
}
}
Jersey客戶端接口詳解
1 Client接口
創(chuàng)建一個Client實例是通過ClientBuilder構(gòu)造的,通常使用一個ClientConfig實例作為參數(shù),如果我們使用Client client = ClientBuilder.newClient()的方式來創(chuàng)建Client實例的時候,每次都會創(chuàng)建一個Client實例,但該實例是一個重量級的對象,所以,建議使用HTTP連接池的方式來管理連接,而不是每次請求都去創(chuàng)建一個Client對象,具體的連接池管理方式見上面的代碼示例。
2 WebTarget接口
WebTarget接口是為REST客戶端實現(xiàn)資源定位的接口,通過WebTarget接口,我們可以定義請求資源的具體地址,查詢參數(shù)和媒體類型信息等。我們可以通過方法鏈的方式完成對一個WebTarget實例的配置,但是需要注意的是,雖然WebTarget的使用方式和StringBuffer的方法鏈方式非常類似,但實質(zhì)是不一樣的,WebTarget的方法鏈必須設(shè)置方法的返回值,作為后續(xù)流程的句柄,這個是什么意思了,看下面的幾個示例:
示例1:StringBuffer的方法鏈示例
StringBuffer sb = new StringBuffer("lch");
sb.append("hello");
sb.append("world");
sb.append("hello").append("world"); // 這種方式和上面的兩行代碼實現(xiàn)的效果是一樣的。
示例2:WebTarget的方法鏈示例
// 使用一行代碼的方法鏈來實例化WebTarget
WebTarget webTarget = client.target("http://localhost:8080");
webTarget.path("/github/get/users/page")
.queryParam("pageOffset", pageOffset)
.queryParam("pageSize", pageSize)
.queryParam("orderColumn", orderColumn);
// 下面是分開使用方法鏈來實例化WebTarget
webTarget.path("/github/get/users/page");
webTarget.queryParam("pageOffset", pageOffset);
webTarget.queryParam("pageSize", pageSize);
// 上面兩種實例化的方式最后產(chǎn)生的結(jié)果大相徑庭,上面的實例化方式是OK的,沒有問題,下面的實例化方式卻有問題,下面的實例化方式中,每一行都會生成一個
// 新的WebTarget對象,原來的WebTarget并沒有起任何作用,畢竟每一行的實例都不一樣,如果我們想要分多行實例化了,就必須為每個方法的返回提供一個句柄,方式如下:
WebTarget target = client.target("http://localhost:8080");
WebTarget pathTarget = target.path("/github/get/users/page");
WebTarget paramTarget = pathTarget.queryParam("pageOffset", pageOffset);
// 最后使用的時候,用最后一個WebTarget實例對象即可
3 Invocation接口
Invocation接口是在完成資源定位配置后,向REST服務(wù)端發(fā)起請求的接口,請求包括同步和異步兩種方式,由Invocation接口內(nèi)部的Builder接口定義,Builder接口繼承了同步接口SyncInvoker,異步調(diào)用的使用示例如下:
Future<ResultMsg<List<GitHubEntity>>> response = invocationBuilder.async().get(genericType);
if(response.isDone()){
return response.get();
}
Invocation.Builder接口實例分別執(zhí)行了GET和POST請求來提交查詢和創(chuàng)建,默認情況下,HTTP方法調(diào)用的返回類型是Response類型,同時也支持泛型類型的返回值,在上面的示例中,我們使用了大量的泛型,這里就不做過多的解釋了。
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
java高并發(fā)的volatile與Java內(nèi)存模型詳解
這篇文章主要介紹了java高并發(fā)的volatile與Java內(nèi)存模型,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2021-10-10
java集合List快速實現(xiàn)重復判斷的方法小結(jié)
在java編寫代碼中經(jīng)常會遇到某些重復判定或者去重的操作,本文主要為大家介紹了幾個常用方法,感興趣的小伙伴可以跟隨不想一起學習一下2024-12-12
SpringBoot如何使用@Cacheable進行緩存與取值
這篇文章主要介紹了SpringBoot如何使用@Cacheable進行緩存與取值,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-08-08
springboot動態(tài)注入配置與docker設(shè)置環(huán)境變量的方法
這篇文章主要介紹了springboot動態(tài)注入配置與docker設(shè)置環(huán)境變量的方法,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-04-04
Java實現(xiàn)短信驗證碼和國際短信群發(fā)功能的示例
本篇文章主要介紹了Java實現(xiàn)短信驗證碼和國際短信群發(fā)功能的示例,具有一定的參考價值,感興趣的小伙伴們可以參考一下。2017-02-02

