目录
- 概述
- 使用
- 配置
- pom.xml
- feign 接口编写
- controller
- 测试
- 降级处理
- pom.xml
- application.yml
- 代码
- Feign如何初始化及调用源码阅读
- 初始化
- 调用
- feign的上下文隔离机制
- 源码
- 结束
概述
阅读此文,可以知晓 feign 使用、上下文隔离及源码阅读。源码涉及两方面:feign如何初始化及如何调用。
使用
配置
- pom.xm 配置 jar 包
- 启动类添加注解 @EnableFeignClients
- feign 接口编写
pom.xml
增加以下两个 jar
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency><dependency><groupId>io.github.openfeign</groupId><artifactId>feign-httpclient</artifactId>
</dependency>
feign 接口编写
package com.fun.ms.feign;import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;@FeignClient("nacos-provider")
public interface OpenFeignProviderControllerFeignClient {@GetMapping("/hello/{id}")String echo(@PathVariable String id);
}
controller
controller 中使用 feign 调用。
@Autowired
private OpenFeignProviderControllerFeignClient feignClient;@GetMapping("/hello/feign/{id}")
public String echoFeign(@PathVariable String id) {return feignClient.echo(id);
}
测试
测试如下:
http://localhost:9060/hello/feign/helloword
降级处理
官方文档
**注意:**以下
测试接口:
http://localhost:9060/hello/feign/helloword
pom.xml
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-hystrix</artifactId><version>2.2.10.RELEASE</version>
</dependency>
application.yml
feign:circuitbreaker:enabled: true
代码
feign 接口
@FeignClient(value = "nacos-provider", fallback = OpenFeignProviderControllerFeignClientFallback.class)
public interface OpenFeignProviderControllerFeignClient {@GetMapping("/hello/{id}")String echo(@PathVariable String id);
}
fallback
@Component
public class OpenFeignProviderControllerFeignClientFallback implements OpenFeignProviderControllerFeignClient {@Overridepublic String echo(String id) {return "Fallback receive args: id=" + id + ",date:"+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());}
}
测试 如下
Feign如何初始化及调用源码阅读
初始化
初始化关键要明白以下两点:
- FeignClientsRegistrar ImportBeanDefinitionRegistrar 如何注入 @FeignClient
- 如何动态代理生成对象 FeignClientFactoryBean
关键切入点
org.springframework.cloud.openfeign.EnableFeignClients
断点关键处如下:
org.springframework.cloud.openfeign.FeignClientsRegistrar#registerBeanDefinitions
org.springframework.cloud.openfeign.FeignClientsRegistrar#registerFeignClient
org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#scanCandidateComponents
org.springframework.cloud.openfeign.FeignClientsRegistrar#registerOptionsBeanDefinition
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#obtainFromSupplier
org.springframework.cloud.openfeign.FeignClientFactoryBean#getObject
org.springframework.cloud.openfeign.FeignClientFactoryBean#loadBalance
org.springframework.cloud.openfeign.FeignClientFactoryBean#get
org.springframework.cloud.openfeign.DefaultTargeter#target
feign.ReflectiveFeign#newInstance
feign.InvocationHandlerFactory.Default#create
关键:feign.ReflectiveFeign#newInstance,动态代理
调用
关键地方如下:
feign.ReflectiveFeign.FeignInvocationHandler#invoke
feign的上下文隔离机制
理解 feign 的上下文隔离机制,有助于我们对 feign 的使用有更深的理解。
源码
关键断点设置如下:
org.springframework.cloud.openfeign.FeignClientFactoryBean#getTarget
org.springframework.cloud.openfeign.FeignAutoConfiguration#feignContext
org.springframework.cloud.openfeign.FeignClientFactoryBean#feign
org.springframework.cloud.openfeign.FeignClientFactoryBean#get
org.springframework.cloud.context.named.NamedContextFactory#getContext
org.springframework.cloud.context.named.NamedContextFactory#createContext
几个关键点
org.springframework.cloud.openfeign.FeignAutoConfiguration
重要源码,feign 实现上下方隔离 的原理在以下代码中。
protected AnnotationConfigApplicationContext createContext(String name) {AnnotationConfigApplicationContext context;if (this.parent != null) {DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();......省略一些代码.......// 走这context = new AnnotationConfigApplicationContext(beanFactory);context.setClassLoader(this.parent.getClassLoader());}else {context = new AnnotationConfigApplicationContext();}if (this.configurations.containsKey(name)) {for (Class<?> configuration : this.configurations.get(name).getConfiguration()) {context.register(configuration);}}for (Map.Entry<String, C> entry : this.configurations.entrySet()) {if (entry.getKey().startsWith("default.")) {for (Class<?> configuration : entry.getValue().getConfiguration()) {context.register(configuration);}}}context.register(PropertyPlaceholderAutoConfiguration.class, this.defaultConfigType);context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(this.propertySourceName,Collections.<String, Object>singletonMap(this.propertyName, name)));if (this.parent != null) {// Uses Environment from parent as well as beanscontext.setParent(this.parent);}context.setDisplayName(generateDisplayName(name));// 实例化此 spring 容器,servlet 类型 spring 容器作为父容器context.refresh();return context;
}
由上述代码可知,有 feign 的项目,启动时长会更长的,因为每个 provider feign
都会生成一个 spring 容器。spring 容器初始化是比较耗时的。
结束
Feign
使用及源码阅读至此结束,如有问题,欢迎评论区留言。