详解如何使用Jersey客户端请求Spring Boot(RESTFul)服务

网友投稿 522 2023-02-27


详解如何使用Jersey客户端请求Spring Boot(RESTFul)服务

本文介绍了使用Jersey客户端请求Spring Boot(RESTFul)服务,分享给大家,具体如下:

Jersey客户端获取Client对象实例封装:

@Service("jerseyPoolingClient")

public class JerseyPoolingClientFactoryBean implements FactoryBean, 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服务的封装:

@Component("jerseyClient")

public class JerseyClient {

@Resource(name="jerseyPoolingClient")

private Client client;

/**

* attention:

* Details:通过id来查询对象

* @author chhliu

*/

public ResultMsg 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> genericType = new GenericType>(){};

Response response = invocationBuilder.get();

if(response.getStatus() == 200){

/*

* 当调用readEntity方法时,程序会自动的释放连接

* 即使没有调用readEntity方法,直接返回泛型类型的对象,底层仍然会释放连接

*/

return response.readEntity(genericType);

}else{

ResultMsg res = new ResultMsg();

res.setErrorCode(String.valueOf(response.getStatus()));

res.setErrorMsg(response.getStatusInfo().toString());

res.setOK(false);

return res;

}

}

/**

* attention:

* Details:分页查询

* @author chhliu

*/

public ResultMsg> 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>> genericType = new GenericType>>(){};

Response response = invocationBuilder.get();

if(response.getStatus() == 200){

return response.readEntity(genericType);

}else{

ResultMsg> res = new ResultMsg>();

res.setErrorCode(String.valueOf(response.getStatus()));

res.setErrorMsg(response.getStatusInfo().toString());

res.setOK(false);

return res;

}

}

/**

* attention:

* Details:根据用户名来查询

* @author chhliu

*/

public ResultMsg> 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>> genericType = new GenericType>>(){};

Response response = invocationBuilder.get();

if(response.getStatus() == 200){

return response.readEntity(genericType);

}else{

ResultMsg> res = new ResultMsg>();

res.setErrorCode(String.valueOf(response.getStatus()));

res.setErrorMsg(response.getStatusInfo().toString());

res.setOK(false);

return res;

}

}

/**

* attention:

* Details:根据id来删除一个记录

* @author chhliu

*/

public ResultMsg deleteById(final String id) throws JsonProcessingException, IOException{

WebTarget target = client.target("http://localhost:8080").path("/github/delete/"+id);

GenericType> genericType = new GenericType>(){};

Response response = target.request().delete();

if(response.getStatus() == 200){

return response.readEntity(genericType);

}else{

ResultMsg res = new ResultMsg();

res.setErrorCode(String.valueOf(response.getStatus()));

res.setErrorMsg(response.getStatusInfo().toString());

res.setOK(false);

return res;

}

}

/**

* attention:

* Details:更新一条记录

* @author chhliu

*/

public ResultMsg update(final GitHubEntity entity) throws JsonProcessingException, IOException{

WebTarget target = client.target("http://localhost:8080").path("/github/put");

GenericType> genericType = new GenericType>(){};

Response response = target.request().buildPut(Entity.entity(entity, MediaType.APPLICATION_JSON)).invoke();

if(response.getStatus() == 200){

return response.readEntity(genericType);

}else{

ResultMsg res = new ResultMsg();

res.setErrorCode(String.valueOf(response.getStatus()));

res.setErrorMsg(response.getStatusInfo().toString());

res.setOK(false);

return res;

}

}

/**

* attention:

* Details:插入一条记录

* @author chhliu

*/

public ResultMsg save(final GitHubEntity entity) throws JsonProcessingException, IOException{

WebTarget target = client.target("http://localhost:8080").path("/github/post");

GenericType> genericType = new GenericType>(){};

Response response = target.request().buildPost(Entity.entity(entity, MediaType.APPLICATION_JSON)).invoke();

if(response.getStatus() == 200){

return response.readEntity(genericType);

}else{

ResultMsg res = new ResultMsg();

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的方法链示例

StringBuffer sb = new StringBuffer("lch");

sb.append("hello");

sb.append("world");

sb.append("hello").append("world"); // 这种方式和上面的两行代码实现的效果是一样的。

示例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);

// 上面两种实例化的方式最后产生的结果大相径庭,上面的实例化方式是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,异步调用的使用示例如下:

Future>> response = invocationBuilder.async().get(genericType);

if(response.isDone()){

return response.get();

}

Invocation.Builder接口实例分别执行了GET和POST请求来提交查询和创建,默认情况下,HTTP方法调用的返回类型是Response类型,同时也支持泛型类型的返回值,在上面的示例中,我们使用了大量的泛型,这里就不做过多的解释了。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:接口自动化测试平台
下一篇:api监控
相关文章

 发表评论

暂时没有评论,来抢沙发吧~