@TOC
Feign是Netflix开发的声明式、模板化的HTTP客户端。其实之前我有点误解,觉得netflix的很多组件,脱离springCloud之后就都没什么好用的了。但是对feign了解了之后,觉得这feign是个特例把。
一、feign原理
feign本质就是一个HTTP请求客户端,发起HTTP请求。只是他对HTTP客户端进行了声明式、模板化的规范。在发起HTTP请求时、时不时的需要加个请求头、加个拦截器、设置请求方法、封装参数、解析结果等,这些过程大部分时候都是要定制的。如果自己写,就会要不断的修改代码或者一大堆的if-else,而feign就是对这一过程进行了封装,可以用拔插式的开发方式对请求过程进行组装。
那feign是如何进行封装的呢?其实就是两步,把请求的过程和请求的结果分开。feign把不太会经常变化的请求参数、结果、访问路径封装到服务接口类中,而将变化较多的请求处理过程封装到feign的客户端中。下面就一起去看看把。
二、简单例子
例子还是可以参见GIT上的DEMO项目。
首先上一个SpringBoot的服务端的Controller,这里面定义了四种常见类型的接口。
@RestController
@RequestMapping(value="/user")
public class UserController {
@RequestMapping(value="/getUser")
public List<User> getUser(@RequestParam int i){
//implement codes
return res;
}
@RequestMapping(value="/getOneUser")
public User getOneUsers(@RequestBody User user) {
//implement codes
return res;
}
@RequestMapping(value="/getStringInfo")
public String getStringInfo(@RequestParam String test) {
//implement codes
return res;
}
@RequestMapping(value="/hello",method= {RequestMethod.GET,RequestMethod.POST})
public String hello() {
//implement codes
return res;
}
这些接口,客户端去访问很简单,部署在本机的话,访问http://localhost:8081/user/**,就可以了。那下面用feign来访问。
首先定义服务接口,服务接口中需要定义请求的参数、结果以及访问地址。可以看到,这一部分其实是不会有什么变化的。麻烦一点的是要注意下Feign自己的注解。一般都很少用到。例如针对Map类型的请求参数,还要使用@QueryMap。这些用到的时候查查就行了。
public interface IUserService {
@RequestLine(value = "GET /user/hello")
public String hello();
@RequestLine(value="GET /user/getStringInfo?test={test}")
public String getStringInfo(@Param("test") String test);
@RequestLine(value="POST /user/getOneUser")
@Headers("Content-Type: application/json")
public User getOneUser(User user);
@RequestLine(value="POST /user/getUser?i={count}")
public List<User> getUsers(@Param("count") int count);
}
然后定义feign客户端,客户端中定义了请求处理的各个过程。可以看到,针对不同类型的服务接口都是不同的。
public static void main(String[] args) {
//1、普通接口访问
IUserService basicService = Feign.builder()
.target(IUserService.class, "http://localhost:8081");
System.out.println(basicService.hello());
System.out.println(basicService.getStringInfo("BasicFeign"));
//2、结果转码器
IUserService gsonService = Feign.builder()
.decoder(new GsonDecoder())
.target(IUserService.class, "http://localhost:8081");
List<User> users = gsonService.getUsers(4);
for(User user:users) {
System.out.println(JSON.toJSONString(user));
}
//3、参数转码器
IUserService gsonService2 = Feign.builder()
.encoder(new GsonEncoder())
.decoder(new GsonDecoder())
.target(IUserService.class, "http://localhost:8081");
User user = new User();
User oneUser = gsonService2.getOneUser(user);
System.out.println(JSON.toJSONString(oneUser));
//4、客户端-注意要是feign包中的客户端
IUserService gsonService3 = Feign.builder()
.client(new OkHttpClient())
.encoder(new GsonEncoder())
.decoder(new GsonDecoder())
.target(IUserService.class, "http://localhost:8081");
User oneUser2 = gsonService3.getOneUser(user);
System.out.println(JSON.toJSONString(oneUser2));
//5、注解翻译器
}
这其中,Feign中定义了访问的根路径 http://localhost:8081,其实,这里不定义根路径也是可以的。
三、深入feign组件
对于feign请求接口,就只需要注意下feign的注解就行了。重点看看feign如何通过组件化来装配Http请求的处理过程。feign在定义了这些组装接口的同时,还提供了很多的默认实现,提供了很好的扩展。当然,要自己扩展也是可以的。
--feign提供组件的maven坐标都是:com.netflix.feign:feign-*(groupId:artifactId 格式。如feign的核心库坐标为 com.netflix.feign:feign-core)
1、Encoder 与 Decoder
Encoder实现了如何封装请求参数,统一实现feign.codec.Encoder接口,而Decoder实现了如何封装请求结果。
feign提供了com.netflix.feign:feign-gson(GsonEncoder和GsonDecoder) 和 com.netflix.feign:feign-jackson(JacksonEncoder和JacksonDecoder)来实现对JSON格式的转换支持。还有 com.netflix.feign:feign-sax(SaxDecoder)解析xml,com.netflix.feign:feign-jaxb(JAXBEncoder和JAXBDecoder)
2、client 客户端
client客户端统一实现feign.Client接口。这里面就有一个execute方法需要实现,可以看到定义了如何去发起请求。
feign提供了com.netflix.feign:feign-okhttp(OkHttpClient)组件提供对OkHttpClient的支持。 还有 com.netflix.feign:feign-ribbon(RibbonClient.create())组件提供对ribbon的支持(这个就熟悉了把)。
3、Constract 注解翻译器
Constract是注解翻译器组件,这个东东的作用是如何解析访问接口Feign以外的注解。例如我们在SpringCloud中几乎不会使用Feign的@RequestLine这一类注解,这其中就是注解翻译器做的事情。 例如有com.netflix.feign:feign-jaxr实现使用JAX-RS格式的注解。
4、requestInterceptor 请求拦截器
Feign.builder()中,还提供了requestInterceptor和requestInterceptors方法,可以给http请求注入拦截器,可以做一些统一设置http的Header、meta之类的事情。
5、logger与logLevel
feign默认是不会记录接口请求日志的,如果需要对接口日志进行记录,需要设置Feign.bulder()的logger和logLevel属性。
例如 com.netflix.feign:feign-slf4j(Slf4jLogger)来实现对Log4j的支持。
四、SpringCloud结合feign,了解SpringCloud为我们干了些什么。
SpringCloud中使用feign是很常见的,这里就不去介绍了。要了解的可以看看Demo就行了。其实,看了上面的介绍,主要是要了解下SpringCloud在这后面为我们做了些什么事情。
例如,SpringCloud是提供了SpringMvcContract注释翻译器,让Feign支持SpringMVC的注解。--这其中,共提供了类注解:@FeignClient,方法注解:@RequestMapping,@RequestParam,@RequestHeader四个注解的翻译。
还引入了ribbon,给feign提供了负载均衡支持。也就同样可以通过注入IRule、IClient等接口来扩展负载均衡策略。
在starter中也引入了feign-hystrix,提供了对hystrix的支持。这些就可以慢慢去摸索了。