feign实现远程服务调用,根据接口及接口方法上的注解解析生成代理。
由于我们在启动类上加了EnableFeignClients注解,根据spring的Import注解机制,我们可以发现FeignClientsRegistrar.registerBeanDefinitions会注册FeignClientSpecification(当defaultConfiguration不为null时)和FeignClient。
FeignClient生成代码如下:
# spring-cloud-openfeign-core-2.2.7.RELEASE.jar!/org.springframework.cloud.openfeign.FeignClientsRegistrar
public void registerFeignClients(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();
Map<String, Object> attrs = metadata
.getAnnotationAttributes(EnableFeignClients.class.getName());
final Class<?>[] clients = attrs == null ? null
: (Class<?>[]) attrs.get("clients");
if (clients == null || clients.length == 0) { // 1. 默认clients为空数组
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
Set<String> basePackages = getBasePackages(metadata); // 2. EnableFeignClients的value/basePackages/basePackageClasses不为空则作为扫描的基础包,否则取被EnableFeignClients注解的类所在包
for (String basePackage : basePackages) {
candidateComponents.addAll(scanner.findCandidateComponents(basePackage)); // 3. 将有FeignClient注解的类生成BeanDefinition
}
}
else {
for (Class<?> clazz : clients) {
candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz));
}
}
for (BeanDefinition candidateComponent : candidateComponents) {
if (candidateComponent instanceof AnnotatedBeanDefinition) {
// verify annotated class is an interface
AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
Assert.isTrue(annotationMetadata.isInterface(),
"@FeignClient can only be specified on an interface");
Map<String, Object> attributes = annotationMetadata
.getAnnotationAttributes(FeignClient.class.getCanonicalName());
String name = getClientName(attributes); // 4. 按照contextId、value、name、serviceId从高到低优先级拿属性值取名(属性支持占位符)
registerClientConfiguration(registry, name,
attributes.get("configuration")); // 5. 根据@FeignClient的configuration属性(默认空数组),注册FeignClientSpecification
// 6. 以FeignClientFactoryBean作为feignClientsRegistrarFactoryBean,接口类型作为factoryBeanObjectType注册BeanDefinition;
// 如果qualifiers不存在,则以“http:”或“https:”,开头contextId/serviceId/name/value为中间,以“FeignClient”结尾,组合作为bean名称;按类型自动注入。
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
这些FeignClientSpecification会被注入FeignContext作为配置(见FeignAutoConfiguration)。
spring上下文刷新时会触发生成接口对象,由于是FactoryBean形式,可以直接查看FeignClientFactoryBean.getObject代码:
# spring-cloud-openfeign-core-2.2.7.RELEASE.jar!/org.springframework.cloud.openfeign.FeignClientFactoryBean
@Override
public Object getObject() {
return getTarget();
}
<T> T getTarget() {
FeignContext context = beanFactory != null
? beanFactory.getBean(FeignContext.class)
: applicationContext.getBean(FeignContext.class); // 1. FeignClientFactoryBean实现了ApplicationContextAware、BeanFactoryAware接口, FeignContext在FeignAutoConfiguration定义
// 2. FeignContext的默认配置为FeignClientsConfiguration,它定义了默认的FeignLoggerFactory、Feign.Builder、Encoder、Decoder、Contract、FeignBuilderCustomizer、FeignClientConfigurer;如果@EnableFeignClients/@EnableFeignClient有自定义配置类也会注册到FeignContext;
// 从FeignContext获取FeignLoggerFactory、Feign.Builder、Encoder、Decoder、Contract、FeignBuilderCustomizer、FeignClientConfigurer,从ApplicationContext获取FeignClientProperties等,设置Feign.Builder;
// 默认先根据配置类设置,然后用配置文件的默认属性进行覆盖,最后再用配置文件的特定client属性覆盖;
// 如果存在FeignBuilderCustomizer,则最后使用FeignBuilderCustomizer.customize(默认不存在)
Feign.Builder builder = feign(context);
if (!StringUtils.hasText(url)) { // 3. 测试直连时可能会设置url,使用客户端负载均衡不要设置
if (!name.startsWith("http")) {
url = "http://" + name;
}
else {
url = name;
}
url += cleanPath();
return (T) loadBalance(builder, context,
new HardCodedTarget<>(type, name, url)); // 4. 创建ReflectiveFeign,调用其newInstance方法,返回代理
}
if (StringUtils.hasText(url) && !url.startsWith("http")) {
url = "http://" + 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();
}
if (client instanceof FeignBlockingLoadBalancerClient) {
// not load balancing because we have a url,
// but Spring Cloud LoadBalancer is on the classpath, so unwrap
client = ((FeignBlockingLoadBalancerClient) client).getDelegate();
}
builder.client(client);
}
Targeter targeter = get(context, Targeter.class);
return (T) targeter.target(this, builder, context,
new HardCodedTarget<>(type, name, url));
}
target为DefaultTargeter,如果hystrix存在时则为HystrixTargeter。DefaultTargeter只是简单地调用Feign.Builder的target方法,此方法生成ReflectiveFeign,然后生成代理,代码如下:
# feign-core-10.10.1.jar!/feign.ReflectiveFeign
public <T> T newInstance(Target<T> target) {
Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target); // 1. 在Feign中创建的ParseHandlersByName,此时调用其apply方法,contract(SpringMvcContract)解析类和方法上的注解信息,使用SynchronousMethodHandler.Factory创建SynchronousMethodHandler
Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
for (Method method : target.type().getMethods()) { // 2. 遍历接口方法
if (method.getDeclaringClass() == Object.class) {
continue;
} else if (Util.isDefault(method)) {
DefaultMethodHandler handler = new DefaultMethodHandler(method);
defaultMethodHandlers.add(handler);
methodToHandler.put(method, handler);
} else {
methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method))); // 3. 为接口的方法创建对应的处理方法
}
}
InvocationHandler handler = factory.create(target, methodToHandler); // 4. handler为根据Method找到对应的MethodHandler(SynchronousMethodHandler),然后调用
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), // 5. 使用JDK动态代理生成代理对象
new Class<?>[] {target.type()}, handler);
for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
defaultMethodHandler.bindTo(proxy);
}
return proxy;
}
当调用发生时,根据方法会分派到实际的MethodHandler,即SynchronousMethodHandler。
# feign-core-10.10.1.jar!/feign.SynchronousMethodHandler
@Override
public Object invoke(Object[] argv) throws Throwable {
// 1. 见ReflectiveFeign$ParseHandlersByName如何创建BuildTemplateByResolvingArgs,BuildTemplateByResolvingArgs有Encoder,根据form、body、requestString等不同形式创建RequestTemplate
RequestTemplate template = buildTemplateFromArgs.create(argv);
Options options = findOptions(argv);
Retryer retryer = this.retryer.clone();
while (true) {
try {
// 2. 发出HTTP请求,并对结果进行处理
return executeAndDecode(template, options);
} catch (RetryableException e) {
try {
retryer.continueOrPropagate(e); // 3. 可重试异常时,当重试次数小于最大可重试次数时,线程休眠一段时间后重试,否则抛出异常
} catch (RetryableException th) {
Throwable cause = th.getCause();
if (propagationPolicy == UNWRAP && cause != null) {
throw cause;
} else {
throw th;
}
}
if (logLevel != Logger.Level.NONE) {
logger.logRetry(metadata.configKey(), logLevel);
}
continue;
}
}
}
Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
Request request = targetRequest(template); // 2.1 执行RequestInterceptor.apply;执行HardCodedTarget.apply
if (logLevel != Logger.Level.NONE) {
logger.logRequest(metadata.configKey(), logLevel, request);
}
Response response;
long start = System.nanoTime();
try {
response = client.execute(request, options); // 2.2 使用apache http/okhttp等执行http请求
// ensure the request is set. TODO: remove in Feign 12
response = response.toBuilder()
.request(request)
.requestTemplate(template)
.build();
} catch (IOException e) {
if (logLevel != Logger.Level.NONE) {
logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));
}
throw errorExecuting(request, e);
}
long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
if (decoder != null)
return decoder.decode(response, metadata.returnType()); //2.3 将请求结果根据方法返回类型用Decoder进行处理
CompletableFuture<Object> resultFuture = new CompletableFuture<>();
asyncResponseHandler.handleResponse(resultFuture, metadata.configKey(), response,
metadata.returnType(),
elapsedTime);
try {
if (!resultFuture.isDone())
throw new IllegalStateException("Response handling not done");
return resultFuture.join();
} catch (CompletionException e) {
Throwable cause = e.getCause();
if (cause != null)
throw cause;
throw e;
}
}
关于Client接口,feign有默认的实现是feign.Client.Default。它直接使用jdk的HttpURLConnection来发送请求和处理响应。另外feign也提供了apache http client适配类以及okHttp适配类。
ribbon提供了AbstractLoadBalancerAwareClient,而spring cloud netflix提供了LoadBalancerFeignClient。
本文暂时没有评论,来添加一个吧(●'◡'●)