共计 7468 个字符,预计需要花费 19 分钟才能阅读完成。
序
本文主要研究一下 spring cloud 的 FeignClientFactoryBean
FeignClientFactoryBean
spring-cloud-openfeign-core-2.2.0.M1-sources.jar!/org/springframework/cloud/openfeign/FeignClientFactoryBean.java
class FeignClientFactoryBean | |
implements FactoryBean<Object>, InitializingBean, ApplicationContextAware { | |
/*********************************** | |
* WARNING! Nothing in this class should be @Autowired. It causes NPEs because of some | |
* lifecycle race condition. | |
***********************************/ | |
private Class<?> type; | |
private String name; | |
private String url; | |
private String contextId; | |
private String path; | |
private boolean decode404; | |
private ApplicationContext applicationContext; | |
private Class<?> fallback = void.class; | |
private Class<?> fallbackFactory = void.class; | |
@Override | |
public void afterPropertiesSet() throws Exception {Assert.hasText(this.contextId, "Context id must be set"); | |
Assert.hasText(this.name, "Name must be set"); | |
} | |
@Override | |
public Object getObject() throws Exception {return getTarget(); | |
} | |
@Override | |
public Class<?> getObjectType() {return this.type;} | |
@Override | |
public boolean isSingleton() {return true;} | |
@Override | |
public void setApplicationContext(ApplicationContext context) throws BeansException {this.applicationContext = context;} | |
<T> T getTarget() {FeignContext context = this.applicationContext.getBean(FeignContext.class); | |
Feign.Builder builder = feign(context); | |
if (!StringUtils.hasText(this.url)) {if (!this.name.startsWith("http")) {this.url = "http://" + this.name;} | |
else {this.url = this.name;} | |
this.url += cleanPath(); | |
return (T) loadBalance(builder, context, | |
new HardCodedTarget<>(this.type, this.name, this.url)); | |
} | |
if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {this.url = "http://" + this.url;} | |
String url = this.url + cleanPath(); | |
Client client = getOptional(context, Client.class); | |
if (client != null) {if (client instanceof LoadBalancerFeignClient) { | |
// not load balancing because we have a url, | |
// but ribbon is on the classpath, so unwrap | |
client = ((LoadBalancerFeignClient) client).getDelegate();} | |
builder.client(client); | |
} | |
Targeter targeter = get(context, Targeter.class); | |
return (T) targeter.target(this, builder, context, | |
new HardCodedTarget<>(this.type, this.name, url)); | |
} | |
private String cleanPath() {String path = this.path.trim(); | |
if (StringUtils.hasLength(path)) {if (!path.startsWith("/")) {path = "/" + path;} | |
if (path.endsWith("/")) {path = path.substring(0, path.length() - 1); | |
} | |
} | |
return path; | |
} | |
//...... | |
} |
- FeignClientFactoryBean 实现了 FactoryBean 的 getObject、getObjectType、isSingleton 方法;实现了 InitializingBean 的 afterPropertiesSet 方法;实现了 ApplicationContextAware 的 setApplicationContext 方法
- getObject 调用的是 getTarget 方法,它从 applicationContext 取出 FeignContext,然后构造 Feign.Builder 并设置了 logger、encoder、decoder、contract,之后通过 configureFeign 根据 FeignClientProperties 来进一步配置 Feign.Builder 的 retryer、errorDecoder、request.Options、requestInterceptors、queryMapEncoder、decode404
- 初步配置完 Feign.Builder 之后再判断是否需要 loadBalance,如果需要则通过 loadBalance 方法来设置,不需要则在 Client 是 LoadBalancerFeignClient 的时候进行 unwrap
FeignClientProperties
spring-cloud-openfeign-core-2.2.0.M1-sources.jar!/org/springframework/cloud/openfeign/FeignClientProperties.java
@ConfigurationProperties("feign.client") | |
public class FeignClientProperties { | |
private boolean defaultToProperties = true; | |
private String defaultConfig = "default"; | |
private Map<String, FeignClientConfiguration> config = new HashMap<>(); | |
public boolean isDefaultToProperties() {return this.defaultToProperties;} | |
public void setDefaultToProperties(boolean defaultToProperties) {this.defaultToProperties = defaultToProperties;} | |
public String getDefaultConfig() {return this.defaultConfig;} | |
public void setDefaultConfig(String defaultConfig) {this.defaultConfig = defaultConfig;} | |
public Map<String, FeignClientConfiguration> getConfig() {return this.config;} | |
public void setConfig(Map<String, FeignClientConfiguration> config) {this.config = config;} | |
@Override | |
public boolean equals(Object o) {if (this == o) {return true;} | |
if (o == null || getClass() != o.getClass()) {return false;} | |
FeignClientProperties that = (FeignClientProperties) o; | |
return this.defaultToProperties == that.defaultToProperties | |
&& Objects.equals(this.defaultConfig, that.defaultConfig) | |
&& Objects.equals(this.config, that.config); | |
} | |
@Override | |
public int hashCode() {return Objects.hash(this.defaultToProperties, this.defaultConfig, this.config); | |
} | |
/** | |
* Feign client configuration. | |
*/ | |
public static class FeignClientConfiguration { | |
private Logger.Level loggerLevel; | |
private Integer connectTimeout; | |
private Integer readTimeout; | |
private Class<Retryer> retryer; | |
private Class<ErrorDecoder> errorDecoder; | |
private List<Class<RequestInterceptor>> requestInterceptors; | |
private Boolean decode404; | |
private Class<Decoder> decoder; | |
private Class<Encoder> encoder; | |
private Class<Contract> contract; | |
public Logger.Level getLoggerLevel() {return this.loggerLevel;} | |
public void setLoggerLevel(Logger.Level loggerLevel) {this.loggerLevel = loggerLevel;} | |
public Integer getConnectTimeout() {return this.connectTimeout;} | |
public void setConnectTimeout(Integer connectTimeout) {this.connectTimeout = connectTimeout;} | |
public Integer getReadTimeout() {return this.readTimeout;} | |
public void setReadTimeout(Integer readTimeout) {this.readTimeout = readTimeout;} | |
public Class<Retryer> getRetryer() {return this.retryer;} | |
public void setRetryer(Class<Retryer> retryer) {this.retryer = retryer;} | |
public Class<ErrorDecoder> getErrorDecoder() {return this.errorDecoder;} | |
public void setErrorDecoder(Class<ErrorDecoder> errorDecoder) {this.errorDecoder = errorDecoder;} | |
public List<Class<RequestInterceptor>> getRequestInterceptors() {return this.requestInterceptors;} | |
public void setRequestInterceptors(List<Class<RequestInterceptor>> requestInterceptors) {this.requestInterceptors = requestInterceptors;} | |
public Boolean getDecode404() {return this.decode404;} | |
public void setDecode404(Boolean decode404) {this.decode404 = decode404;} | |
public Class<Decoder> getDecoder() {return this.decoder;} | |
public void setDecoder(Class<Decoder> decoder) {this.decoder = decoder;} | |
public Class<Encoder> getEncoder() {return this.encoder;} | |
public void setEncoder(Class<Encoder> encoder) {this.encoder = encoder;} | |
public Class<Contract> getContract() {return this.contract;} | |
public void setContract(Class<Contract> contract) {this.contract = contract;} | |
@Override | |
public boolean equals(Object o) {if (this == o) {return true;} | |
if (o == null || getClass() != o.getClass()) {return false;} | |
FeignClientConfiguration that = (FeignClientConfiguration) o; | |
return this.loggerLevel == that.loggerLevel | |
&& Objects.equals(this.connectTimeout, that.connectTimeout) | |
&& Objects.equals(this.readTimeout, that.readTimeout) | |
&& Objects.equals(this.retryer, that.retryer) | |
&& Objects.equals(this.errorDecoder, that.errorDecoder) | |
&& Objects.equals(this.requestInterceptors, that.requestInterceptors) | |
&& Objects.equals(this.decode404, that.decode404) | |
&& Objects.equals(this.encoder, that.encoder) | |
&& Objects.equals(this.decoder, that.decoder) | |
&& Objects.equals(this.contract, that.contract); | |
} | |
@Override | |
public int hashCode() { | |
return Objects.hash(this.loggerLevel, this.connectTimeout, this.readTimeout, | |
this.retryer, this.errorDecoder, this.requestInterceptors, | |
this.decode404, this.encoder, this.decoder, this.contract); | |
} | |
} | |
} |
- FeignClientProperties 有个 Map 结构的 config,key 是 feign client 的名称,默认是 default,value 是 FeignClientConfiguration;FeignClientConfiguration 包含了 loggerLevel、connectTimeout、readTimeout、retryer、errorDecoder、requestInterceptors、decode404、decoder、encoder、contract 属性
小结
- FeignClientFactoryBean 实现了 FactoryBean 的 getObject、getObjectType、isSingleton 方法;实现了 InitializingBean 的 afterPropertiesSet 方法;实现了 ApplicationContextAware 的 setApplicationContext 方法
- getObject 调用的是 getTarget 方法,它从 applicationContext 取出 FeignContext,然后构造 Feign.Builder 并设置了 logger、encoder、decoder、contract,之后通过 configureFeign 根据 FeignClientProperties 来进一步配置 Feign.Builder 的 retryer、errorDecoder、request.Options、requestInterceptors、queryMapEncoder、decode404
- 初步配置完 Feign.Builder 之后再判断是否需要 loadBalance,如果需要则通过 loadBalance 方法来设置,不需要则在 Client 是 LoadBalancerFeignClient 的时候进行 unwrap
doc
- FeignClientFactoryBean
正文完