Feign是一个声明式的web service客户端。它让写web service客户端更加容易,要使用Feign只需创建一些接口和一些注解便可。它已经支持Feign和JAX-RS注解并且是可插拔的。Fegin同样支持可插拔的编码器和解码器。Spring cloud为Feign添加了Spring MVC的注解。Spring Cloud整合了Ribbon和Eureka以提供负载均衡的能力。
为了更好的说明Feign的使用方法,我们复制一份microservice-simple-consumer-movie工程,命名为microservice-simple-consumer-movie-feign,修改microservice-simple-consumer-movie-feign工程的pom.xml文件,将原来的"microservice-simple-consumer-movie"更换为"microservice-simple-consumer-movie-feign"如下图。
然后我们编辑父工程的pom.xml文件,添加模块microservice-simple-consumer-movie-feign,如下图所示。
下面我们把MovieController中原来的代码变成如下
package com.itmuch.cloud.Controller;
import com.itmuch.cloud.entity.User;
import com.sun.org.apache.xpath.internal.SourceTree;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
/**
* Created by wanghaijie on 2017/11/19.
*/
@RestController
public class MovieController {
@GetMapping(value = "/movie/{id}")
public User findById(@PathVariable Long id){
return null;
}
}
把application.yml文件中原来的负载均衡代码去掉,修改后如下所示
server:
port: 8010
spring:
application:
name: microservice-consumer-movie
eureka:
client:
healthcheck:
enabled: true
serviceUrl:
defaultZone: http://user:password123@localhost:8761/eureka
instance:
prefer-ip-address: true
把MicroserviceSimpleConsumerMovieApplication这个类中原来的负载均衡注解去掉,修改后如下所示
package com.itmuch.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableEurekaClient
public class MicroserviceSimpleConsumerMovieApplication {
public static void main(String[] args) {
SpringApplication.run(MicroserviceSimpleConsumerMovieApplication.class, args);
}
}
现在这个工程就比较干净了,下面我们来添加feign的依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
下面我们将工程的启动类类名将原来的MicroserviceSimpleConsumerMovieApplication修改为ConsumerFeignMovieApplication并且添加注解@EnableFeignClients如下图所示:
下面新建一个接口类,类的位置与启动类在同一目录。
package com.itmuch.cloud;
import com.itmuch.cloud.entity.User;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* Created by wanghaijie on 2017/12/3.
*/
@FeignClient("microservice-provider-user")
public interface UserFeignClient {
@GetMapping("/simple/{id}")
public User findById(@PathVariable Long id);
}
接着我们在MovieController使用我们的接口,如下图所示。
好,下面我们"尝试"启动一下movie-feigin工程,会报下面的错误。
Caused by: java.lang.IllegalStateException: Method findById not annotated with HTTP method type (ex. GET, POST)
at feign.Util.checkState(Util.java:128) ~[feign-core-9.3.1.jar:na]
at feign.Contract$BaseContract.parseAndValidateMetadata(Contract.java:97) ~[feign-core-9.3.1.jar:na]
at org.springframework.cloud.netflix.feign.support.SpringMvcContract.parseAndValidateMetadata(SpringMvcContract.java:133) ~[spring-cloud-netflix-core-1.2.5.RELEASE.jar:1.2.5.RELEASE]
at feign.Contract$BaseContract.parseAndValidatateMetadata(Contract.java:64) ~[feign-core-9.3.1.jar:na]
at feign.hystrix.HystrixDelegatingContract.parseAndValidatateMetadata(HystrixDelegatingContract.java:34) ~[feign-hystrix-9.3.1.jar:na]
at feign.ReflectiveFeign$ParseHandlersByName.apply(ReflectiveFeign.java:146) ~[feign-core-9.3.1.jar:na]
出现这种错误是因为Feign不支持@GetMapping,它认为我们没有指定是GET请求还是POST请求,我们必须明确指定是GET请求,因此我们在UserFeignClient接口类得使用@RequestMapping这个注解。如下图所示
下面我们再来启动movie-feign工程,会发现又报错了,错误信息如下
Caused by: java.lang.IllegalStateException: PathVariable annotation was empty on param 0.
at feign.Util.checkState(Util.java:128) ~[feign-core-9.3.1.jar:na]
at org.springframework.cloud.netflix.feign.annotation.PathVariableParameterProcessor.processArgument(PathVariableParameterProcessor.java:49) ~[spring-cloud-netflix-core-1.2.5.RELEASE.jar:1.2.5.RELEASE]
at org.springframework.cloud.netflix.feign.support.SpringMvcContract.processAnnotationsOnParameter(SpringMvcContract.java:237) ~[spring-cloud-netflix-core-1.2.5.RELEASE.jar:1.2.5.RELEASE]
at feign.Contract$BaseContract.parseAndValidateMetadata(Contract.java:107) ~[feign-core-9.3.1.jar:na]
出现这个错误是因为我们必须指定接收的参数的名字,我们要接收的是"id",因此我们在@PathVariable后面加上("id"),如下图所示。
上面两个问题是使用feign的两个坑,大家使用的时候一定要注意。
下面我们来访问http://localhost:8010/movie/1,发现正常访问到结果了。
下面来写一个POST请求
现在先在user工程的UserController中添加如下方法
@PostMapping("/user")
public User postUser(@RequestBody User user){
return user;
}
接着在movie-feign工程的UserFeignClient添加
@RequestMapping(value = "/user", method = RequestMethod.POST)
public User postUser(User user);
接着在movie-feign工程的MovieController中添加如下代码。
@GetMapping(value = "/test")
public User testPost(User user){
User user1 = this.userFeignClient.postUser(user);
return user1;
}
下面我们便依次重启user工程和movie工程,然后我们便可以访问http://localhost:8010/test?id=1&name=zhangsan如下图所示。