Appearance
第5章 REST客户端Feign
本章要点
- REST 客户端
- Feign 框架的使用
- 在 Spring Cloud 中使用 Feign
5.1 REST客户端
在SpringCloud集群中,各个角色的通信基于REST服务,因此在调用服务时,就不可避免地需要使用REST服务的请求客户端。前面的章节中使用了Spring自带的RestTemplate,RestTemplate使用HttpClient发送请求。本章中,我们将介绍另一个REST客户端:Feign。
Feign框架已经被集成到Spring Cloud的Netflix项目中,使用该框架可以在Spring Cloud的集群中更加简单地调用REST服务。
5.1.1 使用CXF调用REST服务
CXF是目前一个较为流行的Web Service框架,是Apache的一个开源项目。使用CXF可以发布和调用使用各种协议的服务,包括SOAP协议、XML/HTTP等。当前CXF已经对REST 风格的Web Service提供支持,可以发布或调用REST风格的Web Service。由于CXF可以与Spring进行整合使用并且配置简单,因此得到许多开发者的青睐,而笔者以往所在公司的大部分项目,均使用CXF来发布和调用WebService。本章所使用的CXF版本为3.1.10,在Maven中加入以下依赖:
xml
<!-- CXF -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-core</artifactId>
<version>3.1.10</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-client</artifactId>
<version>3.1.10</version>
</dependency>
编写代码请求/person/ {personld}服务,请见代码清单 5-1 。
文件:Demo4Book/SCN/05/5.1/rest-client/src/main/java/org/crazyit/cloud/CxfClient.java
java
package org.crazyit.cloud;
import java.io.InputStream;
import javax.ws.rs.core.Response;
import org.apache.cxf.helpers.IOUtils;
import org.apache.cxf.jaxrs.client.WebClient;
public class CxfClient {
public static void main(String[] args) throws Exception {
// 创建WebClient
WebClient client = WebClient.create("http://localhost:8080/person/1");
// 获取响应
Response response = client.get();
// 获取响应内容
InputStream ent = (InputStream) response.getEntity();
String content = IOUtils.readStringFromStream(ent);
// 输出字符串
System.out.println(content);
}
}
客户端中使用了 WebClient 类发送请求,获取响应后读取输入流,获取服务返回的 JSON 字符串。运行代码清单 5-1 ,可看到返回的信息。
5.1.2 使用Restlet调用REST服务
Restlet 是一个轻量级的 REST 框架,使用它可以发布和调用 REST 风格的 Web Service 。本小节中的例子所使用的版本为 2.3.10, Maven 依赖如下:
xml
<!-- Restlet -->
<dependency>
<groupId>org.restlet.jee</groupId>
<artifactId>org.restlet</artifactId>
<version>2.3.10</version>
</dependency>
<dependency>
<groupId>org.restlet.jee</groupId>
<artifactId>org.restlet.ext.jackson</artifactId>
<version>2.3.10</version>
</dependency>
客户端实现请见代码清单 5-2 。
java
package org.crazyit.cloud;
import java.util.HashMap;
import java.util.Map;
import org.restlet.data.MediaType;
import org.restlet.ext.jackson.JacksonRepresentation;
import org.restlet.representation.Representation;
import org.restlet.resource.ClientResource;
public class RestletClient {
public static void main(String[] args) throws Exception {
ClientResource client = new ClientResource("http://localhost:8080/person/1");
// 调用get方法,服务器发布的是GET
Representation response = client.get(MediaType.APPLICATION_JSON);
// 创建JacksonRepresentation实例,将响应转换为Map
JacksonRepresentation jr = new JacksonRepresentation(response,HashMap.class);
// 获取转换后的Map对象
Map result = (HashMap) jr.getObject();
// 输出结果
System.out.println(result.get("id") + "-" + result.get("name") + "-"
+ result.get("age") + "-" + result.get("message"));
}
}
代码清单 5-2 中使用的 Restiet 的 API 较为简单, 在此不赘述 。 但需要注意的是,在Maven 中使用 Restlet , 要额外配置仓库地址, 笔者成书时 , 在 Apache 官方仓库中并没有Restlet 的包。在项目的 pom. xml 文件 中增加以下配置 :
xml
<repositories>
<repository>
<id>maven-restlet</id>
<name>Restlet repository</name>
<url>http://maven.restlet.org</url>
</repository>
</repositories>
5.1.3 Feign框架介绍
Feign 是 GitHub 上的一个开源项目,目的是简化 Web Service 客户端的开发 。 在使用Feign 时,可以使用注解来修饰接口,被注解修饰的接口具有访问 Web Service 的能力 。
这些注解中既包括 Feign 自带的注解,也支持使用第三方的注解 。 除此之外, Feign 还支持插件式的编码器和解码器,使用者可以通过该特性对请求和响应进行不同 的封装与解析 。
Spring Cloud 将 Feign 集成到 Netflix 项目中,当与 Eureka 、 Ribbon 集成时, Feign 就具有负载均衡的功能。 Feign 本身在使用上的简便性,加上与 Spring Cloud 的高度整合,使用该框架在 Spring Cloud 中调用集群服务,将会大大降低开发的工作量。
5.1.4 第一个Feign程序
先使用Feign编写一个HelloWorld的客户端,访问服务端的/hello服务,得到返回的字符串。当前SpringCloud所依赖的Feign版本为9.5.0,本章案例中的Feign也使用该版本。建立名称为feign-client的Maven项目,加入以下依赖:
xml
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-core</artifactId>
<version>9.5.0</version>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-gson</artifactId>
<version>9.5.0</version>
</dependency>
新建接口 HelloClient,请见代码清单 5 -3 。
java
package org.crazyit.cloud;
import feign.RequestLine;
public interface HelloClient {
@RequestLine("GET /hello")
String sayHello();
}
HelloClient 表示一个服务接口,在接口的 sayHello 方法中使用了@RequestLine 注解,表示使用 GET 方法向/hello 发送请求。接下来编写客户端的运行类,请见代码清单 5-4 。
java
package org.crazyit.cloud;
import feign.Feign;
import feign.gson.GsonDecoder;
public class HelloMain {
public static void main(String[] args) {
// 调用Hello接口
HelloClient hello = Feign.builder().target(HelloClient.class, "http://localhost:8080/");
System.out.println(hello.getClass().getName());
System.out.println(hello.sayHello());
}
}
在运行类中,使用Feign创建HelloClient接口的实例,最后调用接口定义的方法。运行代码清单5-4,可以看到返回的“HelloWorld”字符串,可见接口已经被调用。熟悉AOP的朋友大概已经猜到,Feign实际上会帮我们动态生成代理类。Feign使用的是JDK的动态代理,生成的代理类会将请求的信息封装,交给feign.Client接口发送请求,而该接口的默认实现类最终会使用java.net.HttpURLConnection来发送HTTP请求。
5.1.5 请求参数与返回对象
本案例中有两个服务,另外一个地址为/person/{personId),需要传入参数并且返回JSON字符串。编写第二个Feign客户端,调用该服务。新建PersonClient服务类,定义调用接口并添加注解,请见代码清单5-5。
java
package org.crazyit.cloud;
import lombok.Data;
import lombok.NoArgsConstructor;
import feign.Param;
import feign.RequestLine;
public interface PersonClient {
@RequestLine("GET /person/{personId}")
Person findById(@Param("personId") Integer personId);
@Data // 为所有属性加上setter和getter等方法
class Person {
Integer id;
String name;
Integer age;
String message;
}
}
定义的接口名称为 findById,参数为personId。需要注意的是,由于会返回Person实例,我们在接口中定义了一个Person的类;为了减少代码量,使用了Lombok项目,使用了该项目的@Data注解。要使用Lombok,需要添加以下Maven依赖:
xml
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
</dependency>
准备好提供服务的客户端类后,再编写运行类 。 运行类基本上与前面的 Hello World 类似,请见代码清单 5-6 。
java
package org.crazyit.cloud;
import org.crazyit.cloud.PersonClient.Person;
import feign.Feign;
import feign.gson.GsonDecoder;
public class PersonMain {
public static void main(String[] args) {
PersonClient personService = Feign.builder()
.decoder(new GsonDecoder())
.target(PersonClient.class, "http://localhost:8080/");
Person person = personService.findById(2);
System.out.println(person.id);
System.out.println(person.name);
System.out.println(person.age);
System.out.println(person.message);
}
}
在调用 Person 服务的运行类中添加了解码器的配置, GsonDecoder 会将返回的 JSON 字符串转换为接口方法返回的对象,关于解码器的内容,将在后面章节中讲述。运行代码清单 5-6 ,可以看到最终的输出。
本节使用了 CXF 、 Restlet、 Feign 来编写 REST 客户端,在编写客户端的过程中, 可以看到 Feign 的代码更加“面向对象”, 至于是否更加简沽,则见仁见智 。 下面的章节, 将深入了解 Feign 的各项功能 。