本文介紹了使用Jersey客戶端請求Spring Boot(RESTFul)服務,分享給大家,具體如下:
Jersey客戶端獲取Client對象實例封裝:
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服務器通信。Client被定義為一個重量級的對象,其內部管理著 * 客戶端通信底層的各種對象,比如連接器,解析器等。因此,不推薦在應用中產生大量的的Client實例,這一點在開發中 * 需要特別小心,另外該接口要求其實例要有關閉連接的保障,否則會造成內存泄露 */ private Client client; /** * 一個Client最大的連接數,默認為2000 */ private int maxTotal = 2000 ; /** * 每路由的默認最大連接數 */ private int defaultMaxPerRoute = 1000 ; private ClientConfig clientConfig; public JerseyPoolingClientFactoryBean() { } /** * 帶配置的構造函數 * @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的構造函數,則該類的實例為null,則使用默認的配置初始化 if ( this .clientConfig == null ){ final ClientConfig clientConfig = new ClientConfig(); // 連接池管理實例,該類是線程安全的,支持多并發操作 PoolingHttpClientConnectionManager pcm = new PoolingHttpClientConnectionManager(); pcm.setMaxTotal( this .maxTotal); pcm.setDefaultMaxPerRoute( this .defaultMaxPerRoute); clientConfig.property(ApacheClientProperties.CONNECTION_MANAGER, pcm); /* * 在使用Jersey來請求Spring Boot服務時,Spring Boot默認使用Jackson來解析JSON * 而Jersey默認使用MOXy解析JSON,當Jersey Client想Spring Boot服務請求資源時, * 這個差異會導致服務端和客戶端對POJO的轉換不同,造成反序列化的錯誤 * 因此,此處需要在Client的Config實例中注冊Jackson特性 */ clientConfig.register(JacksonFeature.class); // 使用配置Apache連接器,默認連接器為HttpUrlConnector clientConfig.connectorProvider(new ApacheConnectorProvider()); client = ClientBuilder.newClient(clientConfig); }else{ // 使用構造函數中的ClientConfig來初始化Client對象 client = ClientBuilder.newClient(this.clientConfig); } } /** * attention: * Details:返回Client對象,如果該對象為null,則創建一個默認的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服務的封裝:
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:通過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 ){ /* * 當調用readEntity方法時,程序會自動的釋放連接 * 即使沒有調用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,那么對應的服務中的參數前需加上@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:根據用戶名來查詢 * @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:根據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接口
創建一個Client實例是通過ClientBuilder構造的,通常使用一個ClientConfig實例作為參數,如果我們使用Client client = ClientBuilder.newClient()的方式來創建Client實例的時候,每次都會創建一個Client實例,但該實例是一個重量級的對象,所以,建議使用HTTP連接池的方式來管理連接,而不是每次請求都去創建一個Client對象,具體的連接池管理方式見上面的代碼示例。
2 WebTarget接口
WebTarget接口是為REST客戶端實現資源定位的接口,通過WebTarget接口,我們可以定義請求資源的具體地址,查詢參數和媒體類型信息等。我們可以通過方法鏈的方式完成對一個WebTarget實例的配置,但是需要注意的是,雖然WebTarget的使用方式和StringBuffer的方法鏈方式非常類似,但實質是不一樣的,WebTarget的方法鏈必須設置方法的返回值,作為后續流程的句柄,這個是什么意思了,看下面的幾個示例:
示例1:StringBuffer的方法鏈示例
1
2
3
4
|
StringBuffer sb = new StringBuffer( "lch" ); sb.append( "hello" ); sb.append( "world" ); sb.append( "hello" ).append( "world" ); // 這種方式和上面的兩行代碼實現的效果是一樣的。 |
示例2:WebTarget的方法鏈示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
// 使用一行代碼的方法鏈來實例化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); // 上面兩種實例化的方式最后產生的結果大相徑庭,上面的實例化方式是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服務端發起請求的接口,請求包括同步和異步兩種方式,由Invocation接口內部的Builder接口定義,Builder接口繼承了同步接口SyncInvoker,異步調用的使用示例如下:
1
2
3
4
5
|
Future<ResultMsg<List<GitHubEntity>>> response = invocationBuilder.async().get(genericType); if (response.isDone()){ return response.get(); } |
Invocation.Builder接口實例分別執行了GET和POST請求來提交查詢和創建,默認情況下,HTTP方法調用的返回類型是Response類型,同時也支持泛型類型的返回值,在上面的示例中,我們使用了大量的泛型,這里就不做過多的解釋了。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:http://blog.csdn.net/liuchuanhong1/article/details/53537874