【Java】Feign - 源码分析

例子这次先不写了。。。直接源码走起。部分设计跟Ribbon一样,这边就不在累述,建议先看Ribbon系列。
依然从spring.factories说起。注意到这里有这几个类:FeignAutoConfiguration、FeignRibbonClientAutoConfiguration。

启动

FeignAutoConfiguration

加载FeignContext,这里会赋值FeignClientSpecification的集合,
后面说FeignClientSpecification类怎么来的,其实跟Ribbon用的是同一个方法。
FeignContext的defaultConfigType是FeignClientsConfiguration.class,这个和Ribboon用法一样,后面会加载。

@Autowired(required = false)

private List<FeignClientSpecification> configurations = new ArrayList<>();

@Bean

public FeignContext feignContext() {

FeignContext context = new FeignContext();

context.setConfigurations(this.configurations);

return context;

}

另一个加载的是HystrixTargeter,虽然是HystrixTargeter,如果Feign.Builder不是feign.hystrix.HystrixFeign.Builder,后面调用的还是Feign.Builder的target方法。

@Configuration(proxyBeanMethods = false)

@ConditionalOnClass(name = "feign.hystrix.HystrixFeign")

protected static class HystrixFeignTargeterConfiguration {

@Bean

@ConditionalOnMissingBean

public Targeter feignTargeter() {

return new HystrixTargeter();

}

}

FeignRibbonClientAutoConfiguration

加载CachingSpringLoadBalancerFactory,这个类会注入SpringClientFactory,看过了Ribbon,是不是就知道了他要做什么。没错,他会创建一个ILoadBalancer,用于负载均衡。

@Bean

@Primary

@ConditionalOnMissingBean

@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")

public CachingSpringLoadBalancerFactory cachingLBClientFactory(

SpringClientFactory factory) {

return new CachingSpringLoadBalancerFactory(factory);

}

FeignRibbonClientAutoConfiguration还import了DefaultFeignLoadBalancedConfiguration类,在这个类,会创建一个LoadBalancerFeignClient,后面需要的Client就是他了。

@Bean

@ConditionalOnMissingBean

public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,

SpringClientFactory clientFactory) {

return new LoadBalancerFeignClient(new Client.Default(null, null), cachingFactory,

clientFactory);

}

【Java】Feign - 源码分析

@EnableFeignClients

我们使用feign的时候,都会使用这个注解,注解里Import了FeignClientsRegistrar这个类,FeignClientsRegistrar实现了ImportBeanDefinitionRegistrar接口,调用registerBeanDefinitions方法。这个方法做了两件事,一件事是注册FeignClientSpecification类,后面会注入到上面的FeignContext中,这个用法跟Ribbon - 几种自定义负载均衡策略提的一样。另外一件事就是创建FactoryBean。

@Override

public void registerBeanDefinitions(AnnotationMetadata metadata,

BeanDefinitionRegistry registry) {

// 注册FeignClientSpecification类

registerDefaultConfiguration(metadata, registry);

// 通过注解的信息注册FeignClientFactoryBean类,

//这个是FactoryBean,所以容器fresh的时候会调用他的getObject方法

registerFeignClients(metadata, registry);

}

这个方法就会扫描EnableFeignClients和FeignClient的注解,然后注册FeignClientFactoryBean类型的bean,这个是FactoryBean,所以容器fresh的时候会调用他的getObject方法。

public void registerFeignClients(AnnotationMetadata metadata,

BeanDefinitionRegistry registry) {

LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();

// 扫描EnableFeignClients注解

Map<String, Object> attrs = metadata

.getAnnotationAttributes(EnableFeignClients.class.getName());

AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(

FeignClient.class);

final Class<?>[] clients = attrs == null ? null

: (Class<?>[]) attrs.get("clients");

// 如果EnableFeignClients没有配置clients信息,扫描FeignClient注解

if (clients == null || clients.length == 0) {

ClassPathScanningCandidateComponentProvider scanner = getScanner();

scanner.setResourceLoader(this.resourceLoader);

// 扫描FeignClient注解

scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));

Set<String> basePackages = getBasePackages(metadata);

for (String basePackage : basePackages) {

candidateComponents.addAll(scanner.findCandidateComponents(basePackage));

}

}

else {

// 如果配置clients信息,就忽略FeignClient注解,直接读取clients信息

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);

registerClientConfiguration(registry, name,

attributes.get("configuration"));

// 注册FeignClientFactoryBean

registerFeignClient(registry, annotationMetadata, attributes);

}

}

}

FeignClientFactoryBean#getObject

getObject会调用getTarget方法,他会创建一个Feign.Builder,创建的时候会传FeignContext,这个FeignContext的加载在上面已经提过了。然后调用loadBalance方法返回代理对象,这个代理就是后面远程调用用的。

<T> T getTarget() {

FeignContext context = applicationContext.getBean(FeignContext.class);

Feign.Builder builder = feign(context);

if (!StringUtils.hasText(url)) {

if (!name.startsWith("http")) {

url = "http://" + name;

}

else {

url = name;

}

url += cleanPath();

return (T) loadBalance(builder, context,

new HardCodedTarget<>(type, name, url));

}

// 直接ip的形式,代码略

}

FeignClientFactoryBean#feign

创建一个Feign.Builder,FeignContext#getInstance,这个类继承了NamedContextFactory,流程和ribbon的SpringClientFactory#getContext一样。上面提到FeignContext的defaultConfigType是FeignClientsConfiguration.class,所以还会加载OptionalDecoder解码、SpringEncoder编码、SpringMvcContract,解析MVC注解、NEVER_RETRY重试、DefaultFeignLoggerFactory日志、FeignClientConfigurer、Feign.builder()。

protected Feign.Builder feign(FeignContext context) {

FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);

Logger logger = loggerFactory.create(type);

// @formatter:off

Feign.Builder builder = get(context, Feign.Builder.class)

// required values

.logger(logger)

.encoder(get(context, Encoder.class))

.decoder(get(context, Decoder.class))

.contract(get(context, Contract.class));

// @formatter:on

// 整合配置Feign.Builder

configureFeign(context, builder);

return builder;

}

FeignClientFactoryBean#loadBalance

这里获取Client和Targeter类,Client就是上面的LoadBalancerFeignClient,他是所有共享的。Targeter是HystrixTargeter,他是serviceId私有的。上面已经提了他最终是调用Feign.Builder#target。

protected <T> T loadBalance(Feign.Builder builder, FeignContext context,

HardCodedTarget<T> target) {

Client client = getOptional(context, Client.class);

if (client != null) {

builder.client(client);

Targeter targeter = get(context, Targeter.class);

return targeter.target(this, builder, context, target);

}

// 异常略

}

Feign.Builder#target

build方法是构建一个ReflectiveFeign对象,newInstance是生成代理对象。

public <T> T target(Target<T> target) {

return build().newInstance(target);

}

build方法中,ReflectiveFeign创建的时候传了ParseHandlersByName,InvocationHandlerFactory,SynchronousMethodHandler三个对象。

public Feign build() {

// 其他略

return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);

}

代理对象生成,生成后,后面对代理对象的访问,都会调用FeignInvocationHandler#invoke方法。

public <T> T newInstance(Target<T> target) {

// ParseHandlersByName是通过Contrac用来解析接口定义。

Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);

// 给每个方法创建一个SynchronousMethodHandler对象

Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();

List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();

for (Method method : target.type().getMethods()) {

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)));

}

}

// 创建一个InvocationHandler对象,这里是FeignInvocationHandler

InvocationHandler handler = factory.create(target, methodToHandler);

T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),

new Class<?>[] {target.type()}, handler);

for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {

defaultMethodHandler.bindTo(proxy);

}

return proxy;

}

总结

启动的时候,为每个接口,生成代理对象。
【Java】Feign - 源码分析

方法调用

FeignInvocationHandler#invoke

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

// 其他略

//dispatch.get(method)获取的是SynchronousMethodHandler

return dispatch.get(method).invoke(args);

}

SynchronousMethodHandler#invoke

调用LoadBalancerFeignClient#execute,LoadBalancerFeignClient加载上面已经讲过了。

public Object invoke(Object[] argv) throws Throwable {

RequestTemplate template = buildTemplateFromArgs.create(argv);

// 其他略

return executeAndDecode(template, options);

// 其他略

}

Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {

// 其他略

response = client.execute(request, options);

// 其他略

}

LoadBalancerFeignClient#execute

@Override

public Response execute(Request request, Request.Options options) throws IOException {

try {

// 获取Uri

URI asUri = URI.create(request.url());

// 获取serviceId

String clientName = asUri.getHost();

URI uriWithoutHost = cleanUrl(request.url(), clientName);

FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(

this.delegate, request, uriWithoutHost);

IClientConfig requestConfig = getClientConfig(options, clientName);

// 获取FeignLoadBalancer,通过FeignLoadBalancer调用请求

return lbClient(clientName)

.executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse();

}

catch (ClientException e) {

IOException io = findIOException(e);

if (io != null) {

throw io;

}

throw new RuntimeException(e);

}

}

lbClientFactory是CachingSpringLoadBalancerFactory,所以调用CachingSpringLoadBalancerFactory#create的create方法。
CachingSpringLoadBalancerFactory有个熟悉的SpringClientFactory对象,他负责获取ILoadBalancer、IClientConfig、ServerIntrospector,然后通过这三个构建一个FeignLoadBalancer对象。既然得到了ILoadBalancer,那后续负载均衡的部分就不再继续了,参考Ribbon - 负载均衡流程。

private FeignLoadBalancer lbClient(String clientName) {

return this.lbClientFactory.create(clientName);

}

public FeignLoadBalancer create(String clientName) {

FeignLoadBalancer client = this.cache.get(clientName);

if (client != null) {

return client;

}

IClientConfig config = this.factory.getClientConfig(clientName);

ILoadBalancer lb = this.factory.getLoadBalancer(clientName);

ServerIntrospector serverIntrospector = this.factory.getInstance(clientName,

ServerIntrospector.class);

client = this.loadBalancedRetryFactory != null

? new RetryableFeignLoadBalancer(lb, config, serverIntrospector,

this.loadBalancedRetryFactory)

: new FeignLoadBalancer(lb, config, serverIntrospector);

this.cache.put(clientName, client);

return client;

}

总结

主要是通过代理对象,然后调用Ribbon进行负载均衡。我们这边只讲述了主流程,怎么构建HTTP请求,怎么处理返回结果,都略过了。。。。。
【Java】Feign - 源码分析

以上是 【Java】Feign - 源码分析 的全部内容, 来源链接: utcz.com/a/87894.html

回到顶部