在服务提供者项目microservice-provider增加一个带参数的接口
@GetMapping("/pay_with_params")
public String pay(String orderCode, int totalPrice) {
return "支付成功-->订单编号:" + orderCode + ",金额:" + totalPrice;
}
然后,在服务消费者里面是这么调用的
@GetMapping("/order/without_feign_pay")
public String withoutFeignPay() {
String orderCode = UUID.randomUUID().toString();
int totalPrice = 100;
return restTemplate.getForObject("http://provider-pay/pay_with_params?orderCode=" + orderCode
+ "&totalPrice=" + totalPrice, String.class);
}
或者是这么调用的
@GetMapping("/order/without_feign_pay2")
public String withoutFeignPay2() {
Map paramMap = new HashMap<>();
paramMap.put("orderCode", UUID.randomUUID().toString());
paramMap.put("totalPrice", 200);
return restTemplate.getForObject("http://provider-pay/pay_with_params?orderCode={orderCode}&"
+ "totalPrice={totalPrice}", String.class, paramMap);
}
不管哪一种,在参数多了的情况下,代码就会变得难以维护。这时候,Feign是解决这个问题的利器。
Feign是Netflix开发的声明式、模块化的HTTP客户端,可以帮助我们更加便捷、优雅地调用HTTP API。
Spring Cloud对Feign进行了增强,使Feign除了可以使用自带的注解或者JAX-RS注解以外,还支持了Spring MVC注解,并整合了Ribbon和Eureka,从而使Feign的使用更加方便。
在Spring Cloud中,使用Feign非常简单——创建一个接口,并在接口方法上添加一些注解,代码就完成了。
1)项目的pom.xml增加Feign的依赖。
org.springframework.cloud
spring-cloud-starter-openfeign
2)修改启动类,为其添加@EnableFeignClients注解。
@SpringBootApplication
@EnableFeignClients
public class MicroserviceConsumerFeignApplication {
public static void main(String[] args) {
SpringApplication.run(MicroserviceConsumerFeignApplication.class, args);
}
}
3)创建一个Feign接口,并添加@FeignClient注解。
注意,方法参数必须用@RequestParam标记,否则被认为是RequestBody而抛个数太多的异常。
@FeignClient("provider-pay") // 或者使用url属性指定请求的URL
public interface PayFeignClient {
@GetMapping("/pay_with_params")
public String payWithParams(@RequestParam String orderCode, @RequestParam int totalPrice);
}
@FeignClient注解中的provider-pay是一个任意的客户端名称,用于创建Ribbon负载均衡器(这里使用了Eureka,会被解析成Eureka Server服务注册表中的服务)。还可使用url属性指定请求的URL(URL可以是完整的URL或者主机名)。
4)修改Controller代码,让其调用Feign接口。
@GetMapping("/order/feign_pay")
public String feignPay() {
System.out.println("订单服务开始调用支付服务...");
String orderCode = UUID.randomUUID().toString();
int totalPrice = 300;
return payFeignClient.payWithParams(orderCode, totalPrice);
}
其中,payFeignClient使用@Autowired自动注入即可。
在Spring Cloud中,Feign默认使用的契约是SpringMvcContract,因此它可以使用Spring MVC的注解。我们试一下自定义Feign的配置,让它使用Feign自带的注解进行工作。
1)创建Feign的配置类。
/**
* 该类为Feign的配置类
* 注意:不应该在主应用程序上下文的@ComponentScan中
* @author z_hh
* @time 2019年2月17日
*/
@Configuration
public class FeignConfiguration {
/**
* 将契约改为feign原生的默认契约,这样就可以使用feign自带的注解了
* @return 默认的feign契约
*/
@Bean
public Contract feignContract() {
return new Contract.Default();
}
}
2)在启动类的@ComponentScan将配置类排除,避免该类的配置信息被所有的@FeignClient共享。
@SpringBootApplication
// 排除FeignConfiguration类
@ComponentScan(excludeFilters = @ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE, classes = {FeignConfiguration.class}))
@EnableFeignClients
public class MicroserviceConsumerFeignApplication {
public static void main(String[] args) {
SpringApplication.run(MicroserviceConsumerFeignApplication.class, args);
}
}
3)Feign接口修改如下。使用@FeignClient的configuration属性指定配置类,同时将接口方法上的Spring MVC注解修改为Feign自带的注解。
/**
* 使用@FeignClient的configuration属性,指定feign的配置类
* @author z_hh
* @time 2019年2月17日
*/
@FeignClient(name="provider-pay", configuration=FeignConfiguration.class) // 或者使用url属性指定请求的URL
public interface PayFeignClient2 {
/**
* 使用feign自带的注解@RequestLine
* @param orderCode
* @param totalPrice
* @return
*/
@RequestLine("GET /pay_with_params")
public String payWithParams(@Param("orderCode") String orderCode, @Param("totalPrice") int totalPrice);
}
但是,在启动的时候报了一个错:Method payWithParams not annotated with HTTP method type (ex. GET, POST),不知道什么原因,大家看下是什么问题,知道的话恳请在评论区留下解决办法!
有没有发现,Feign接口的方法签名跟服务提供者的API方法的签名一样
Feign支持继承。所以,可以将公共部分抽取到一个父接口中,从而简化Feign的开发。
比如,将POJO和API抽取到一个公共的项目里,然后服务提供者和服务消费者同时依赖这个公共项目。不过,通常情况下,不建议在服务器端与客户端共享接口,除了紧耦合之外,Feign本身并不使用Spring MVC的工作机制(方法参数映射不被继承)。关于这个,大家仁者见仁,智者见智吧。