目录
- 概述
- 使用
- 配置
- 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 {
@Override
public 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 beans
context.setParent(this.parent);
}
context.setDisplayName(generateDisplayName(name));
// 实例化此 spring 容器,servlet 类型 spring 容器作为父容器
context.refresh();
return context;
}
由上述代码可知,有 feign 的项目,启动时长会更长的,因为每个 provider feign
都会生成一个 spring 容器。spring 容器初始化是比较耗时的。
结束
Feign
使用及源码阅读至此结束,如有问题,欢迎评论区留言。