Fegin是一个声明式的REST调用的客户端,使用Fegin会让REST接口的调用变得更加简单。它提供了各种的HTTP请求模板几乎可以适用于各种接口的调用,通过编写简单的接口调用方式添加简单的注解就可以将HTTP的请求、参数、地址、格式等内容进行设置。
Fegin会完全的代理HTTP请求,使用的时候直接调用对应的方法就可以实现接口的服务请求相关处理。
在Spring Cloud中对于Fegin进行了像是Ribbon一样的封装处理。它支持Spring MVC的各种调用标准和注解标准。可以与Eureka和Ribbon有机的整合实现接口调用和负载均衡等操作。下面我们就来看看在Spring Cloud中如何整合一个Feign。
我们需要在创建的Spring Cloud项目中添加Fegin相关的依赖。在POM文件中添加如下的依赖配置。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
在添加完相应的配置之后,我们需要在应用的主启动类上添加一个@EnableFeignClients注解用来表示这个应用是一个Fegin的的客户端操作。当然也可以在这个注解的basePackages属性中传入需要被扫描的Fegin客户端存在的包路径。
@EnableDiscoveryClient
@EnableFeignClients
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
可以看到我们在启动类上还添加了@EnableDiscoveryClient注解,表示将这个应用以服务的形式注入的Eureka注册中心。来提供其他服务的调用,或者是用来调用其他服务。
首先我们需要知道如何去定义一个Fegin的接口调用。我们可以通过@FeignClient注解来指定这个是一个Fegin的调用客户端操作,代码如下。
@FeignClient(value="eureka-client-user-service")
public interface UserRemoteClient {
@GetMapping("/hello")
String hello();
}
其中value属性指定的值就是我们在Eureka服务注册中心中设置的服务名称,也就是你需要调用哪个服务的接口。
UserRemoteClient 是一个接口类,也就是说在这个类中我们只需要定义一些关于如何调用用户服务的接口规则,具体的调用操作则交给了使用的时候的Controller层,代码如下
@RestController
public class DemoController {
@Autowired
private UserRemoteClient userRemoteClient;
@GetMapping("/getUserInfo")
public String callHello() {
String result = userRemoteClient.hello();
System.out.println(" 调用结果:" + result);
return result;
}
}
从代码中,我们可以看到,代码中并没有像是RestTemplate一样的调用设置,而是直接使用userRemoteClient.hello()方式进行了接口调用。就如同直接调用了用户服务一样。那么我们的Spring Cloud究竟是如何实现对于Feign的管理调用的呢?下面我们就来看一下Fegin的调用原理
首先我们需要关注在上面提到的一个注解@EnableFeignClients,在这个注解的源码中我们会看到如下的一些信息。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
}
会看到使用了@Import的方式引入了一个FeignClientsRegistrar的类,这个类主要就是用来完成对于@FeginClient注解标注类进行容器注入操作。其中比较关键的就是如下的两个方法
这两个方法几乎完成了所有的被标注了@FeginClient注解的类到HTTP请求方法的转换操作。例如其中有如下的代码
private String getUrl(Map<String, Object> attributes) {
String url = resolve((String) attributes.get("url"));
return getUrl(url);
}
static String getUrl(String url) {
if (StringUtils.hasText(url) && !(url.startsWith("#{") && url.contains("}"))) {
if (!url.contains("://")) {
url = "http://" + url;
}
try {
new URL(url);
}
catch (MalformedURLException e) {
throw new IllegalArgumentException(url + " is malformed", e);
}
}
return url;
}
等到完成这些操作之后,其实容器中存在的就是类似于一系列的REST接口调用的实例对象,然后结合之前Ribbon中所讲的内容,我们知道在注册中心中有各种服务的清单。而服务调用者在调用的时候会有这些服务的所有清单,可以从这个清单中根据自己的规则来找到自己对应需要调用的服务。
而根据上面的操作我们可以知道,在配置了FeginClient之后,会将所有的服务都进行封装,我们只需要根据定义好的规则来进行调用即可,就像是上面代码中在Controller中写的一样。
在我们传统的调用中对于一些需要进行配置的内容都需要我们自己编写代码来实现,而使用了Fegin之后这些操作都可以在注入的时候通过前置处理器进行提前的配置,简化了调用的时候的重复代码编写工作。