1.理解(open-Feign是什么)
open-Feign是一个声明式的Http请求客户端,用于快速发起http请求,降低学习成本
在Feign的基础上增加了对SpringMVC 的注解支持
2.简单使用
2.1 引入jar
org.springframework.cloud
spring-cloud-starter-openfeign
2.1.0.RELEASE
2.2 开发服务端,消费端代码
2.2.1 服务端
package com.zy.more.controller;
import com.zy.more.Page;
import com.zy.more.Result;
import com.zy.more.entity.InventoryDO;
import com.zy.more.service.InventoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.web.bind.annotation.*;
/**
* @author: zhangyao
* @create:2020-06-30 19:07
**/
@RestController
@RequestMapping("/inventory")
public class InventoryController {
@Autowired
InventoryService inventoryService
@GetMapping("/{productId}")
public Result getInventoryById(@PathVariable("productId") Integer productId){
return inventoryService.getInventoryById(productId);
}
}
2.2.2 消费端 (开发接口即可)
启动类增加@EnableFeignClients
package com.zy.more;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* @author: zhangyao
* @create:2020-07-01 11:37
**/
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
开发接口
package com.zy.more.feign;
import com.zy.more.Result;
import com.zy.more.entity.InventoryDO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
import javax.persistence.GeneratedValue;
/**
* @author: zhangyao
* @create:2020-07-01 16:42
**/
@FeignClient(name = "inventory",path = "/inventory")
public interface InventoryFeign {
/**
* 根据商品id查询库存信息
* @Date: 2020/7/1 17:02
* @Author: zhangyao
* @Description:
* @param productId:
* @return: com.zy.more.Result
**/
@GetMapping("/{productId}")
public Result getInventoryById(@PathVariable("productId") Integer productId);
}
2.2.3 解释
服务端提供服务,正常开发controller即可,无需做任何改动
消费端只需要加上开发接口,与服务端提供的controller保持一致,再使用@FeignClient @EnableFeignClients启用Feign,当调用接口的时候通过@Autowired注入开发的Feign接口来调用
3.原理分析
openFeign最核心的原理是jdk的动态代理
- 使用@EnabledFeignClients来启用Feign,扫描所有的FeignClient
- 给每一个FeignClient生成一个动态代理对象的Bean
- 当使用这个Bean的时候,在转换为Request请求发送
所以整个OpenFeign可以理解为两大部分
- 动态代理生成Request请求
- 发送Request请求
发送Request请求默认使用java.net包,可以换成okhttp等高可用http客户端
4.源码分析
4.1 扫描注册
通过@EnableFeignClients进入FeignClientRegistrar类,扫描所有的FeignClient,把对应的FeignClient都注册进Spring容器
FeignClientsRegistrar 注册类
具体看这两个方法
//从SpringBoot启动类进入这里,扫描所有的FeignClient类
public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
ClassPathScanningCandidateComponentProvider scanner = this.getScanner();
scanner.setResourceLoader(this.resourceLoader);
//获取@EnableFeignClients注解的属性值 例如basePackage
Map attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(FeignClient.class);
//如果在@EnableFeignClients中配置了clients,这里就会加载出来对应的client
Class[] clients = attrs == null ? null : (Class[])((Class[])attrs.get("clients"));
Object basePackages;
//如果有对应的client就会去配置这些Client
if (clients != null && clients.length != 0) {
final Set clientClasses = new HashSet();
basePackages = new HashSet();
Class[] var9 = clients;
int var10 = clients.length;
for(int var11 = 0; var11 < var10; ++var11) {
Class clazz = var9[var11];
((Set)basePackages).add(ClassUtils.getPackageName(clazz));
clientClasses.add(clazz.getCanonicalName());
}
AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {
protected boolean match(ClassMetadata metadata) {
String cleaned = metadata.getClassName().replaceAll("\\$", ".");
return clientClasses.contains(cleaned);
}
};
scanner.addIncludeFilter(new FeignClientsRegistrar.AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));
} else { //没有client就走这里,获取配置的basePackages
scanner.addIncludeFilter(annotationTypeFilter);
basePackages = this.getBasePackages(metadata);
}
//循环所有的basePackages 扫描每个包下的client
Iterator var17 = ((Set)basePackages).iterator();
while(var17.hasNext()) {
String basePackage = (String)var17.next();
Set candidateComponents = scanner.findCandidateComponents(basePackage);
Iterator var21 = candidateComponents.iterator();
while(var21.hasNext()) {
BeanDefinition candidateComponent = (BeanDefinition)var21.next();
if (candidateComponent instanceof AnnotatedBeanDefinition) {
//循环每个FeignClient 并获取对应的属性
AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition)candidateComponent;
AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
Assert.isTrue(annotationMetadata.isInterface(), "@FeignClient can only be specified on an interface");
Map attributes = annotationMetadata.getAnnotationAttributes(FeignClient.class.getCanonicalName());
String name = this.getClientName(attributes);
this.registerClientConfiguration(registry, name, attributes.get("configuration"));
//把对应的FeignClient 注册进Spring 容器
this.registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
}
//配置每个FeignClient的属性,这里的属性就是获取@FeignClien注解的属性
private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata, Map attributes) {
String className = annotationMetadata.getClassName();
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);
this.validate(attributes);
definition.addPropertyValue("url", this.getUrl(attributes));
definition.addPropertyValue("path", this.getPath(attributes));
String name = this.getName(attributes);
definition.addPropertyValue("name", name);
String contextId = this.getContextId(attributes);
definition.addPropertyValue("contextId", contextId);
definition.addPropertyValue("type", className);
definition.addPropertyValue("decode404", attributes.get("decode404"));
definition.addPropertyValue("fallback", attributes.get("fallback"));
definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
definition.setAutowireMode(2);
String alias = contextId + "FeignClient";
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
boolean primary = (Boolean)attributes.get("primary");
beanDefinition.setPrimary(primary);
String qualifier = this.getQualifier(attributes);
if (StringUtils.hasText(qualifier)) {
alias = qualifier;
}
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, new String[]{alias});
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
4.2 注入调用
当使用@Autowired注入使用FeignClient时,会通过FeignClientFactory工厂类实例化一个jdk动态代理返回,最终通过http客户端调用Ribbon的负载均衡策略生成Request发送网络请求
4.2.1 第一步 FeignClientFactory
FeignClientFactory类
实现了FactoryBean,Spring调用getObject的时候返回一个jdk的动态代理对象
public Object getObject() throws Exception {
return this.getTarget();
}
T getTarget() {
FeignContext context = (FeignContext)this.applicationContext.getBean(FeignContext.class);
Builder builder = this.feign(context);
//如果feignClient没有指定url,就使用name作为url
if (!StringUtils.hasText(this.url)) {
if (!this.name.startsWith("http")) {
this.url = "http://" + this.name;
} else {
this.url = this.name;
}
this.url = this.url + this.cleanPath();
//返回动态代理对象 下文细说
return this.loadBalance(builder, context, new HardCodedTarget(this.type, this.name, this.url));
} else {//如果有url
if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
this.url = "http://" + this.url;
}
String url = this.url + this.cleanPath();
Client client = (Client)this.getOptional(context, Client.class);
if (client != null) {
if (client instanceof LoadBalancerFeignClient) {
client = ((LoadBalancerFeignClient)client).getDelegate();
}
builder.client(client);
}
//同样返回动态代理对象
Targeter targeter = (Targeter)this.get(context, Targeter.class);
return targeter.target(this, builder, context, new HardCodedTarget(this.type, this.name, url));
}
}
4.2.2 第二步 Proxy
看一下动态代理对象的生成,通过上文的loadBalance方法
protected T loadBalance(Builder builder, FeignContext context, HardCodedTarget target) {
Client client = (Client)this.getOptional(context, Client.class);
if (client != null) {
builder.client(client);
Targeter targeter = (Targeter)this.get(context, Targeter.class);
return targeter.target(this, builder, context, target);
} else {
throw new IllegalStateException("No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?");
}
}
我们发现需要首先获取一个Client对象,这个client对象默认使用的是实现了Feign包下的Client接口的LoadBalanceFeignClient类,这个类默认使用了Ribbon的负载均衡发送请求,发送请求就是调用了这个对象中execute方法(下文详细介绍),可以替换其他高性能客户端,再由Feign进行包装,返回一个Builder,最终使用Targeter生成动态代理
详细说明一下Targeter接口
有两个实现类
DefaultTargeter 和 HystrixTargeter 默认实现是后者
public T target(FeignClientFactoryBean factory, Builder feign, FeignContext context, HardCodedTarget target) {
if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
//如果没有使用Hystrix 就走这里
return feign.target(target);
} else {
feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder)feign;
SetterFactory setterFactory = (SetterFactory)this.getOptional(factory.getName(), context, SetterFactory.class);
if (setterFactory != null) {
builder.setterFactory(setterFactory);
}
Class fallback = factory.getFallback();
if (fallback != Void.TYPE) {
return this.targetWithFallback(factory.getName(), context, target, builder, fallback);
} else {
Class fallbackFactory = factory.getFallbackFactory();
return fallbackFactory != Void.TYPE ? this.targetWithFallbackFactory(factory.getName(), context, target, builder, fallbackFactory) : feign.target(target);
}
}
}
接着调用feign.target构造Feign对象
Feign类
public T target(Target target) {
return this.build().newInstance(target);
}
public Feign build() {
Factory synchronousMethodHandlerFactory = new Factory(this.client, this.retryer, this.requestInterceptors, this.logger, this.logLevel, this.decode404, this.closeAfterDecode, this.propagationPolicy);
ParseHandlersByName handlersByName = new ParseHandlersByName(this.contract, this.options, this.encoder, this.decoder, this.queryMapEncoder, this.errorDecoder, synchronousMethodHandlerFactory);
return new ReflectiveFeign(handlersByName, this.invocationHandlerFactory, this.queryMapEncoder);
}
可以看到最终target方法调用了Feign类的继承类ReflectiveFeign的newInstance方法
public T newInstance(Target target) {
Map nameToHandler = this.targetToHandlersByName.apply(target);
Map methodToHandler = new LinkedHashMap();
List defaultMethodHandlers = new LinkedList();
//获取feignClient中的方法
Method[] var5 = target.type().getMethods();
int var6 = var5.length;
//循环Method中的方法,为方法和handler建立对应的映射关系,并放入一个linkedMap
for(int var7 = 0; var7 < var6; ++var7) {
Method method = var5[var7];
if (method.getDeclaringClass() != Object.class) {
if (Util.isDefault(method)) {
DefaultMethodHandler handler = new DefaultMethodHandler(method);
defaultMethodHandlers.add(handler);
methodToHandler.put(method, handler);
} else {
methodToHandler.put(method, (MethodHandler)nameToHandler.get(Feign.configKey(target.type(), method)));
}
}
}
//构建动态代理需要的InvocationHandler
InvocationHandler handler = this.factory.create(target, methodToHandler);
//生成代理对象
T proxy = Proxy.newProxyInstance(target.type().getClassLoader(), new Class[]{target.type()}, handler);
Iterator var12 = defaultMethodHandlers.iterator();
while(var12.hasNext()) {
DefaultMethodHandler defaultMethodHandler = (DefaultMethodHandler)var12.next();
defaultMethodHandler.bindTo(proxy);
}
return proxy;
}
可以看到这里返回的就是jdk的一个代理对象,也是我们第二步最终返回的对象,之后调用的时候就通过这个动态代理对象来调用具体的方法
详细看一下这个方法
每个method都对应的一个DefaultMethodHandle,DefaultMethodHandler默认实现类是SynchronousMethodHandler
当动态代理对象调用一个接口时,就是调用SynchronousMethodHandler的invoke方法
4.2.3 第三步 调用
当真正发送一个请求过来的时候的流程:
调用SynchronousMethodHandler的invoke方法
//通过这个方法中的executeAndDecode发送请求
public Object invoke(Object[] argv) throws Throwable {
RequestTemplate template = this.buildTemplateFromArgs.create(argv);
Retryer retryer = this.retryer.clone();
while(true) {
try {
return this.executeAndDecode(template);
} catch (RetryableException var8) {
RetryableException e = var8;
try {
retryer.continueOrPropagate(e);
} catch (RetryableException var7) {
Throwable cause = var7.getCause();
if (this.propagationPolicy == ExceptionPropagationPolicy.UNWRAP && cause != null) {
throw cause;
}
throw var7;
}
if (this.logLevel != Level.NONE) {
this.logger.logRetry(this.metadata.configKey(), this.logLevel);
}
}
}
}
//发送请求
Object executeAndDecode(RequestTemplate template) throws Throwable {
Request request = this.targetRequest(template);
if (this.logLevel != Level.NONE) {
this.logger.logRequest(this.metadata.configKey(), this.logLevel, request);
}
long start = System.nanoTime();
Response response;
try {
//此处的client就是上文第二步中我们使用到的LoadBalanceFeignClient对象
response = this.client.execute(request, this.options);
} catch (IOException var15) {
if (this.logLevel != Level.NONE) {
this.logger.logIOException(this.metadata.configKey(), this.logLevel, var15, this.elapsedTime(start));
}
throw FeignException.errorExecuting(request, var15);
}
long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
boolean shouldClose = true;
try {
if (this.logLevel != Level.NONE) {
response = this.logger.logAndRebufferResponse(this.metadata.configKey(), this.logLevel, response, elapsedTime);
}
if (Response.class == this.metadata.returnType()) {
Response var18;
if (response.body() == null) {
var18 = response;
return var18;
} else if (response.body().length() != null && (long)response.body().length() <= 8192L) {
byte[] bodyData = Util.toByteArray(response.body().asInputStream());
Response var20 = response.toBuilder().body(bodyData).build();
return var20;
} else {
shouldClose = false;
var18 = response;
return var18;
}
} else {
Object result;
Object var10;
if (response.status() >= 200 && response.status() < 300) {
if (Void.TYPE != this.metadata.returnType()) {
result = this.decode(response);
shouldClose = this.closeAfterDecode;
var10 = result;
return var10;
} else {
result = null;
return result;
}
} else if (this.decode404 && response.status() == 404 && Void.TYPE != this.metadata.returnType()) {
result = this.decode(response);
shouldClose = this.closeAfterDecode;
var10 = result;
return var10;
} else {
throw this.errorDecoder.decode(this.metadata.configKey(), response);
}
}
} catch (IOException var16) {
if (this.logLevel != Level.NONE) {
this.logger.logIOException(this.metadata.configKey(), this.logLevel, var16, elapsedTime);
}
throw FeignException.errorReading(request, response, var16);
} finally {
if (shouldClose) {
Util.ensureClosed(response.body());
}
}
}
到这里我们可以发现,最终发送请求的类就是LoadBalanceFeignClient
4.2.4 第四步 发送请求
发送请求这部分就是ribbon的相关知识了
public Response execute(Request request, Options options) throws IOException {
try {
URI asUri = URI.create(request.url());
String clientName = asUri.getHost();
URI uriWithoutHost = cleanUrl(request.url(), clientName);
RibbonRequest ribbonRequest = new RibbonRequest(this.delegate, request, uriWithoutHost);
IClientConfig requestConfig = this.getClientConfig(options, clientName);
return ((RibbonResponse)this.lbClient(clientName).executeWithLoadBalancer(ribbonRequest, requestConfig)).toResponse();
} catch (ClientException var8) {
IOException io = this.findIOException(var8);
if (io != null) {
throw io;
} else {
throw new RuntimeException(var8);
}
}
}
5.扩展
5.1扩展客户端
修改默认的java.net包下的HttpUrlConnection为HttpClient,OkHttpClient
OkHttpClient
引入jar包
io.github.openfeign
feign-okhttp
11.0
在看 openFeign 的配置类 FeignAutoConfiguration
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.cloud.openfeign;
import feign.Client;
import feign.Feign;
import feign.httpclient.ApacheHttpClient;
import feign.okhttp.OkHttpClient;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;
import javax.annotation.PreDestroy;
import okhttp3.ConnectionPool;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.actuator.HasFeatures;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientConnectionManagerFactory;
import org.springframework.cloud.commons.httpclient.ApacheHttpClientFactory;
import org.springframework.cloud.commons.httpclient.OkHttpClientConnectionPoolFactory;
import org.springframework.cloud.commons.httpclient.OkHttpClientFactory;
import org.springframework.cloud.openfeign.support.FeignHttpClientProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConditionalOnClass({Feign.class})
@EnableConfigurationProperties({FeignClientProperties.class, FeignHttpClientProperties.class})
public class FeignAutoConfiguration {
@Autowired(
required = false
)
private List configurations = new ArrayList();
public FeignAutoConfiguration() {
}
@Bean
public HasFeatures feignFeature() {
return HasFeatures.namedFeature("Feign", Feign.class);
}
@Bean
public FeignContext feignContext() {
FeignContext context = new FeignContext();
context.setConfigurations(this.configurations);
return context;
}
@Configuration
@ConditionalOnClass({OkHttpClient.class})
@ConditionalOnMissingClass({"com.netflix.loadbalancer.ILoadBalancer"})
@ConditionalOnMissingBean({okhttp3.OkHttpClient.class})
@ConditionalOnProperty({"feign.okhttp.enabled"})
protected static class OkHttpFeignConfiguration {
private okhttp3.OkHttpClient okHttpClient;
protected OkHttpFeignConfiguration() {
}
@Bean
@ConditionalOnMissingBean({ConnectionPool.class})
public ConnectionPool httpClientConnectionPool(FeignHttpClientProperties httpClientProperties, OkHttpClientConnectionPoolFactory connectionPoolFactory) {
Integer maxTotalConnections = httpClientProperties.getMaxConnections();
Long timeToLive = httpClientProperties.getTimeToLive();
TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit();
return connectionPoolFactory.create(maxTotalConnections, timeToLive, ttlUnit);
}
@Bean
public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory, ConnectionPool connectionPool, FeignHttpClientProperties httpClientProperties) {
Boolean followRedirects = httpClientProperties.isFollowRedirects();
Integer connectTimeout = httpClientProperties.getConnectionTimeout();
Boolean disableSslValidation = httpClientProperties.isDisableSslValidation();
this.okHttpClient = httpClientFactory.createBuilder(disableSslValidation).connectTimeout((long)connectTimeout, TimeUnit.MILLISECONDS).followRedirects(followRedirects).connectionPool(connectionPool).build();
return this.okHttpClient;
}
@PreDestroy
public void destroy() {
if (this.okHttpClient != null) {
this.okHttpClient.dispatcher().executorService().shutdown();
this.okHttpClient.connectionPool().evictAll();
}
}
@Bean
@ConditionalOnMissingBean({Client.class})
public Client feignClient(okhttp3.OkHttpClient client) {
return new OkHttpClient(client);
}
}
@Configuration
@ConditionalOnClass({ApacheHttpClient.class})
@ConditionalOnMissingClass({"com.netflix.loadbalancer.ILoadBalancer"})
@ConditionalOnMissingBean({CloseableHttpClient.class})
@ConditionalOnProperty(
value = {"feign.httpclient.enabled"},
matchIfMissing = true
)
protected static class HttpClientFeignConfiguration {
private final Timer connectionManagerTimer = new Timer("FeignApacheHttpClientConfiguration.connectionManagerTimer", true);
@Autowired(
required = false
)
private RegistryBuilder registryBuilder;
private CloseableHttpClient httpClient;
protected HttpClientFeignConfiguration() {
}
@Bean
@ConditionalOnMissingBean({HttpClientConnectionManager.class})
public HttpClientConnectionManager connectionManager(ApacheHttpClientConnectionManagerFactory connectionManagerFactory, FeignHttpClientProperties httpClientProperties) {
final HttpClientConnectionManager connectionManager = connectionManagerFactory.newConnectionManager(httpClientProperties.isDisableSslValidation(), httpClientProperties.getMaxConnections(), httpClientProperties.getMaxConnectionsPerRoute(), httpClientProperties.getTimeToLive(), httpClientProperties.getTimeToLiveUnit(), this.registryBuilder);
this.connectionManagerTimer.schedule(new TimerTask() {
public void run() {
connectionManager.closeExpiredConnections();
}
}, 30000L, (long)httpClientProperties.getConnectionTimerRepeat());
return connectionManager;
}
@Bean
public CloseableHttpClient httpClient(ApacheHttpClientFactory httpClientFactory, HttpClientConnectionManager httpClientConnectionManager, FeignHttpClientProperties httpClientProperties) {
RequestConfig defaultRequestConfig = RequestConfig.custom().setConnectTimeout(httpClientProperties.getConnectionTimeout()).setRedirectsEnabled(httpClientProperties.isFollowRedirects()).build();
this.httpClient = httpClientFactory.createBuilder().setConnectionManager(httpClientConnectionManager).setDefaultRequestConfig(defaultRequestConfig).build();
return this.httpClient;
}
@Bean
@ConditionalOnMissingBean({Client.class})
public Client feignClient(HttpClient httpClient) {
return new ApacheHttpClient(httpClient);
}
@PreDestroy
public void destroy() throws Exception {
this.connectionManagerTimer.cancel();
if (this.httpClient != null) {
this.httpClient.close();
}
}
}
@Configuration
@ConditionalOnMissingClass({"feign.hystrix.HystrixFeign"})
protected static class DefaultFeignTargeterConfiguration {
protected DefaultFeignTargeterConfiguration() {
}
@Bean
@ConditionalOnMissingBean
public Targeter feignTargeter() {
return new DefaultTargeter();
}
}
@Configuration
@ConditionalOnClass(
name = {"feign.hystrix.HystrixFeign"}
)
protected static class HystrixFeignTargeterConfiguration {
protected HystrixFeignTargeterConfiguration() {
}
@Bean
@ConditionalOnMissingBean
public Targeter feignTargeter() {
return new HystrixTargeter();
}
}
}
就很明显了,想要注入okhttpClient 需要两个条件
- 需要引入我们开始引入的jar包,feign对okHttpClient的适配
- 需要在配置文件中配置feign.okhttp.enabled = true
HttpClient的配置同理
6.对比各个客户端
openFeign |
RestTemplate |
httpClient |
OkHttpClient |
|
负载均衡 |
支持(内嵌Ribbon) |
原生不支持(需要引入Ribbon) |
不支持 |
不支持 |