本文介紹了使用Jersey客戶端請(qǐng)求Spring Boot(RESTFul)服務(wù),分享給大家,具體如下:
Jersey客戶端獲取Client對(duì)象實(shí)例封裝:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
|
@Service ( "jerseyPoolingClient" ) public class JerseyPoolingClientFactoryBean implements FactoryBean<Client>, InitializingBean, DisposableBean{ /** * Client接口是REST客戶端的基本接口,用于和REST服務(wù)器通信。Client被定義為一個(gè)重量級(jí)的對(duì)象,其內(nèi)部管理著 * 客戶端通信底層的各種對(duì)象,比如連接器,解析器等。因此,不推薦在應(yīng)用中產(chǎn)生大量的的Client實(shí)例,這一點(diǎn)在開(kāi)發(fā)中 * 需要特別小心,另外該接口要求其實(shí)例要有關(guān)閉連接的保障,否則會(huì)造成內(nèi)存泄露 */ private Client client; /** * 一個(gè)Client最大的連接數(shù),默認(rèn)為2000 */ private int maxTotal = 2000 ; /** * 每路由的默認(rèn)最大連接數(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:容器銷毀時(shí),釋放Client資源 * @author chhliu */ @Override public void destroy() throws Exception { this .client.close(); } /** * * attention: * Details:以連接池的形式,來(lái)初始化Client對(duì)象 * @author chhliu */ @Override public void afterPropertiesSet() throws Exception { // 如果沒(méi)有使用帶ClientConfig的構(gòu)造函數(shù),則該類的實(shí)例為null,則使用默認(rèn)的配置初始化 if ( this .clientConfig == null ){ final ClientConfig clientConfig = new ClientConfig(); // 連接池管理實(shí)例,該類是線程安全的,支持多并發(fā)操作 PoolingHttpClientConnectionManager pcm = new PoolingHttpClientConnectionManager(); pcm.setMaxTotal( this .maxTotal); pcm.setDefaultMaxPerRoute( this .defaultMaxPerRoute); clientConfig.property(ApacheClientProperties.CONNECTION_MANAGER, pcm); /* * 在使用Jersey來(lái)請(qǐng)求Spring Boot服務(wù)時(shí),Spring Boot默認(rèn)使用Jackson來(lái)解析JSON * 而Jersey默認(rèn)使用MOXy解析JSON,當(dāng)Jersey Client想Spring Boot服務(wù)請(qǐng)求資源時(shí), * 這個(gè)差異會(huì)導(dǎo)致服務(wù)端和客戶端對(duì)POJO的轉(zhuǎn)換不同,造成反序列化的錯(cuò)誤 * 因此,此處需要在Client的Config實(shí)例中注冊(cè)Jackson特性 */ clientConfig.register(JacksonFeature.class); // 使用配置Apache連接器,默認(rèn)連接器為HttpUrlConnector clientConfig.connectorProvider(new ApacheConnectorProvider()); client = ClientBuilder.newClient(clientConfig); }else{ // 使用構(gòu)造函數(shù)中的ClientConfig來(lái)初始化Client對(duì)象 client = ClientBuilder.newClient(this.clientConfig); } } /** * attention: * Details:返回Client對(duì)象,如果該對(duì)象為null,則創(chuàng)建一個(gè)默認(rèn)的Client * @author chhliu */ @Override public Client getObject() throws Exception { if(null == this.client){ return ClientBuilder.newClient(); } return this.client; } /** * attention: * Details:獲取Client對(duì)象的類型 * @author chhliu */ @Override public Class<?> getObjectType() { return (this.client == null ? Client.class : this.client.getClass()); } /** * attention: * Details:Client對(duì)象是否為單例,默認(rèn)為單例 * @author chhliu */ @Override public boolean isSingleton() { return true ; } } |
請(qǐng)求Spring Boot服務(wù)的封裝:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
|
@Component ( "jerseyClient" ) public class JerseyClient { @Resource (name= "jerseyPoolingClient" ) private Client client; /** * attention: * Details:通過(guò)id來(lái)查詢對(duì)象 * @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 ){ /* * 當(dāng)調(diào)用readEntity方法時(shí),程序會(huì)自動(dòng)的釋放連接 * 即使沒(méi)有調(diào)用readEntity方法,直接返回泛型類型的對(duì)象,底層仍然會(huì)釋放連接 */ 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:分頁(yè)查詢 * @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,那么對(duì)應(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ù)用戶名來(lái)查詢 * @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來(lái)刪除一個(gè)記錄 * @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)建一個(gè)Client實(shí)例是通過(guò)ClientBuilder構(gòu)造的,通常使用一個(gè)ClientConfig實(shí)例作為參數(shù),如果我們使用Client client = ClientBuilder.newClient()的方式來(lái)創(chuàng)建Client實(shí)例的時(shí)候,每次都會(huì)創(chuàng)建一個(gè)Client實(shí)例,但該實(shí)例是一個(gè)重量級(jí)的對(duì)象,所以,建議使用HTTP連接池的方式來(lái)管理連接,而不是每次請(qǐng)求都去創(chuàng)建一個(gè)Client對(duì)象,具體的連接池管理方式見(jiàn)上面的代碼示例。
2 WebTarget接口
WebTarget接口是為REST客戶端實(shí)現(xiàn)資源定位的接口,通過(guò)WebTarget接口,我們可以定義請(qǐng)求資源的具體地址,查詢參數(shù)和媒體類型信息等。我們可以通過(guò)方法鏈的方式完成對(duì)一個(gè)WebTarget實(shí)例的配置,但是需要注意的是,雖然WebTarget的使用方式和StringBuffer的方法鏈方式非常類似,但實(shí)質(zhì)是不一樣的,WebTarget的方法鏈必須設(shè)置方法的返回值,作為后續(xù)流程的句柄,這個(gè)是什么意思了,看下面的幾個(gè)示例:
示例1:StringBuffer的方法鏈?zhǔn)纠?/p>
1
2
3
4
|
StringBuffer sb = new StringBuffer( "lch" ); sb.append( "hello" ); sb.append( "world" ); sb.append( "hello" ).append( "world" ); // 這種方式和上面的兩行代碼實(shí)現(xiàn)的效果是一樣的。 |
示例2:WebTarget的方法鏈?zhǔn)纠?/p>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
// 使用一行代碼的方法鏈來(lái)實(shí)例化WebTarget WebTarget webTarget = client.target( "http://localhost:8080" ); webTarget.path( "/github/get/users/page" ) .queryParam( "pageOffset" , pageOffset) .queryParam( "pageSize" , pageSize) .queryParam( "orderColumn" , orderColumn); // 下面是分開(kāi)使用方法鏈來(lái)實(shí)例化WebTarget webTarget.path( "/github/get/users/page" ); webTarget.queryParam( "pageOffset" , pageOffset); webTarget.queryParam( "pageSize" , pageSize); // 上面兩種實(shí)例化的方式最后產(chǎn)生的結(jié)果大相徑庭,上面的實(shí)例化方式是OK的,沒(méi)有問(wèn)題,下面的實(shí)例化方式卻有問(wèn)題,下面的實(shí)例化方式中,每一行都會(huì)生成一個(gè) // 新的WebTarget對(duì)象,原來(lái)的WebTarget并沒(méi)有起任何作用,畢竟每一行的實(shí)例都不一樣,如果我們想要分多行實(shí)例化了,就必須為每個(gè)方法的返回提供一個(gè)句柄,方式如下: WebTarget target = client.target( "http://localhost:8080" ); WebTarget pathTarget = target.path( "/github/get/users/page" ); WebTarget paramTarget = pathTarget.queryParam( "pageOffset" , pageOffset); // 最后使用的時(shí)候,用最后一個(gè)WebTarget實(shí)例對(duì)象即可 |
3 Invocation接口
Invocation接口是在完成資源定位配置后,向REST服務(wù)端發(fā)起請(qǐng)求的接口,請(qǐng)求包括同步和異步兩種方式,由Invocation接口內(nèi)部的Builder接口定義,Builder接口繼承了同步接口SyncInvoker,異步調(diào)用的使用示例如下:
1
2
3
4
5
|
Future<ResultMsg<List<GitHubEntity>>> response = invocationBuilder.async().get(genericType); if (response.isDone()){ return response.get(); } |
Invocation.Builder接口實(shí)例分別執(zhí)行了GET和POST請(qǐng)求來(lái)提交查詢和創(chuàng)建,默認(rèn)情況下,HTTP方法調(diào)用的返回類型是Response類型,同時(shí)也支持泛型類型的返回值,在上面的示例中,我們使用了大量的泛型,這里就不做過(guò)多的解釋了。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持服務(wù)器之家。
原文鏈接:http://blog.csdn.net/liuchuanhong1/article/details/53537874