目錄
- 概述
- 使用
- 配置
- 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
使用及源碼閱讀至此結束,如有問題,歡迎評論區留言。