我们在使用spring cloud ribbon时,通常都会利用它对RestTemplate的拦截来实现对依赖服务的接口调用,而RestTemplate已经实现了对http请求的封装调用,形成了一套模版化的方法。在实际的开发中,由于对微服务的调用可能会有多处,我们通常都会针对各个微服务封装一些客户端类来包装这些服务的调用,但由于RestTemplate的封装,几乎每一个调用都是模版化的内容。针对这些情况,spring cloud feign在此基础上进行了封装,由它来帮助我们定义和实现依赖服务接口的定义,我们只需创建一个接口并用注解的方式配置它,即可完成对服务提供方的接口绑定。
快速入门
在本节中,将通过简单的示例来展现spring cloud feign带来的便利,下面的示例将继续调用之前的hello-service。
首先,新建spring boot工程,并在pom文件中加入之前项目导入过的依赖以及spring cloud feign的依赖
org.springframework.cloud
spring-cloud-starter-openfeign
创建应用主类,并使用@EnableFeignClients注解开启spring cloud feign功能
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients
@EnableEurekaClient
public class FeignConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(FeignConsumerApplication.class, args);
}
}
定义HelloServiceApi接口,通过@FeignClient注解指定服务名来绑定服务,然后使用springMVC注解来绑定具体提供服务的rest接口
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
@FeignClient(name = "hello-service")
public interface HelloServiceAPi {
@RequestMapping("say-hello")
String hello();
}
接着,创建一个controller来实现对feign客户端的调用
import com.example.feignconsumer.api.HelloServiceAPi;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class FiegnConsumerController {
@Autowired
private HelloServiceAPi helloServiceAPi;
@RequestMapping("consumer")
public String consumer() {
return helloServiceAPi.hello();
}
}
最后,需要在application.properties文件中指定服务注册中心以及其他相关信息。
server.port=8998
eureka.client.service-url.defaultZone=http://localhost:1111/eureka
spring.application.name=feign-consumer
继承特性
当使用springMVC的注解来绑定服务接口时,我们几乎完全可以从服务提供方的controller接口复制出相应的客户端绑定接口。在spring cloud feign中,提供了继承特性来帮助我们解决这些复制操作,以减少编码量。
为了能够复用DTO与接口定义,我们先创建一个基础工程,命名为hello-service-api,并提供接口
import com.example.domain.User;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
public interface HelloServiceApi {
@RequestMapping("sayHelloWithUser")
String sayHelloWithUser(@RequestBody User user);
@RequestMapping("sayHello")
String sayHello(@RequestParam("name") String name);
}
下面对hello-service进行重构,在pom文件中将加入对hello-service-api的依赖,创建controller实现定义的接口
@RestController
public class RefactorHelloController implements HelloServiceApi {
@Override
public String sayHelloWithUser(@RequestBody User user) {
return "hello," + user.getName() + ",your age is" + user.getAge();
}
@Override
public String sayHello(@RequestParam("name") String name) {
return "hello " + name;
}
}
接着对feign-consumer进行重构,在pom文件中将加入对hello-service-api的依赖,创建RefactorHelloServiceApi继承HelloServiceApi并加入@ FeignClient注解
@FeignClient(name = "hello-service")
public interface RefactorHelloServiceApi extends HelloServiceApi {
}
最后新建RefactorConsumerController,注入RefactorHelloServiceApi调用服务接口
@RestController
public class RefactorConsumerController {
@Autowired
private RefactorHelloServiceApi RefactorHelloServiceApi;
@RequestMapping("consumeSayHello")
public String sayHello() {
return RefactorHelloServiceApi.sayHello("lisi");
}
@RequestMapping("consumeSayHelloWithUser")
public String sayHelloWithUser() {
User user = new User();
user.setAge(10);
user.setName("zhangsan");
return RefactorHelloServiceApi.sayHelloWithUser(user);
}
}