天天看点

Spring-Cloud Feign解析

Feign是一个声明式的Web服务客户端。这使得Web服务客户端的写入更加方便 要使用Feign创建一个界面并对其进行注释。它具有可插拔注释支持,包括Feign注释和JAX-RS注释。Feign还支持可插拔编码器和解码器。Spring Cloud集成Ribbon和Eureka以在使用Feign时提供负载均衡的http客户端。

原生的feign的用法

<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-gson</artifactId>
    <version>9.5.0</version>
</dependency>
           

基本用法

1、开启注解EnableFeignClients

@EnableFeignClients
public class ApplicationStartup {
    public static void main(String[] args) {
        SpringApplication.run(ApplicationStartup.class, args);
    } 
}
           

2、定义接口

@FeignClient(name = "user")
public interface IUserService{
    @RequestMapping(value = "/api/getUser")
    User getUser(Long id);
}
           

3、这样通过spring注入IUserService就可以使用了

@Controller
public class UserController {
    @Autowired
    private IUserService userService;
}
           

从上述代码可以看出,Feign主要是有两个注解进行切入,分别为:@EnableFeignClients,@FeignClient,下面将对这两个注解进行详细的分析。

1)@EnableFeignClients

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)// 重点在这里,注入FeignClientsRegistrar类
public @interface EnableFeignClients {}
           

  2)FeignClientsRegistrar.java分析

    通过其类结构可知,其实现了ImportBeanDefinitionRegistrar接口,那么在registerBeanDefinitions()中就会注册一些bean到Spring中

class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
		ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware{
        
	@Override
	public void registerBeanDefinitions(AnnotationMetadata metadata,
			BeanDefinitionRegistry registry) {
        // 1.针对那些在@EnableFeignClients中添加了defaultConfiguration属性的进行操作
        // 将这些类定义的bean添加到容器中
		registerDefaultConfiguration(metadata, registry);
        
        // 2.注册那些添加了@FeignClient的类或接口
        // 重点就在这里
		registerFeignClients(metadata, registry);
	}
}
           

    3)registerFeignClients(metadata, registry)方法分析

public void registerFeignClients(AnnotationMetadata metadata,
                                 BeanDefinitionRegistry registry) {
    ClassPathScanningCandidateComponentProvider scanner = getScanner();
    scanner.setResourceLoader(this.resourceLoader);
 
    // 1.以下代码的主要功能是扫描包下的所有带有@FeignClient注解的类
    Set<String> basePackages;
 
    Map<String, Object> attrs = metadata
        .getAnnotationAttributes(EnableFeignClients.class.getName());
    // @FeignClient注解过滤器
    AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
        FeignClient.class);
    final Class<?>[] clients = attrs == null ? null
        : (Class<?>[]) attrs.get("clients");
    if (clients == null || clients.length == 0) {
        // 在这里获取带有@FeignClient注解的类,放在basePackages中
        scanner.addIncludeFilter(annotationTypeFilter);
        basePackages = getBasePackages(metadata);
    }
    else {
        final Set<String> clientClasses = new HashSet<>();
        basePackages = new HashSet<>();
        for (Class<?> clazz : clients) {
            basePackages.add(ClassUtils.getPackageName(clazz));
            clientClasses.add(clazz.getCanonicalName());
        }
        AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {
            @Override
            protected boolean match(ClassMetadata metadata) {
                String cleaned = metadata.getClassName().replaceAll("\\$", ".");
                return clientClasses.contains(cleaned);
            }
        };
        scanner.addIncludeFilter(
            new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));
    }
 
    // 2.针对所有带有@FeignClient注解的类或接口分别封装
    // 注册其Configuration类(如果有的话)
    // 并将类或接口本身注入到Spring中
    for (String basePackage : basePackages) {
        Set<BeanDefinition> candidateComponents = scanner
            .findCandidateComponents(basePackage);
        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);
                // 2.1 注册其Configuration类(如果有的话)
                // 本例中使用的是默认的Configuration,就不再分析该段代码
                registerClientConfiguration(registry, name,
                                            attributes.get("configuration"));
 
                // 2.2 并将类或接口本身注入到Spring中
                registerFeignClient(registry, annotationMetadata, attributes);
            }
        }
    }
}
           

 总结:从3)的分析可知,@EnableFeignClients注解的主要功能就是把带有@FeignClient注解的类或接口注册到Spring中

4.registerFeignClient()方法的分析

private void registerFeignClient(BeanDefinitionRegistry registry,
                                 AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
    // 1.获取类名称,也就是本例中的FeignService接口
    String className = annotationMetadata.getClassName();
   
    // 2.BeanDefinitionBuilder的主要作用就是构建一个AbstractBeanDefinition
    // AbstractBeanDefinition类最终被构建成一个BeanDefinitionHolder
    // 然后注册到Spring中
    // 注意:beanDefinition类为FeignClientFactoryBean,故在Spring获取类的时候实际返回的是
    // FeignClientFactoryBean类
    BeanDefinitionBuilder definition = BeanDefinitionBuilder
        .genericBeanDefinition(FeignClientFactoryBean.class);
    validate(attributes);
    
    // 3.添加FeignClientFactoryBean的属性,
    // 这些属性也都是我们在@FeignClient中定义的属性
    definition.addPropertyValue("url", getUrl(attributes));
    definition.addPropertyValue("path", getPath(attributes));
    String name = getName(attributes);
    definition.addPropertyValue("name", name);
    definition.addPropertyValue("type", className);
    definition.addPropertyValue("decode404", attributes.get("decode404"));
    definition.addPropertyValue("fallback", attributes.get("fallback"));
    definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
    definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
 
    // 4.设置别名 name就是我们在@FeignClient中定义的name属性
    String alias = name + "FeignClient";
    AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
 
    boolean primary = (Boolean)attributes.get("primary"); // has a default, won't be null
 
    beanDefinition.setPrimary(primary);
 
    String qualifier = getQualifier(attributes);
    if (StringUtils.hasText(qualifier)) {
        alias = qualifier;
    }
 
    // 5.定义BeanDefinitionHolder,
    // 在本例中 名称为FeignService,类为FeignClientFactoryBean
    BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
                                                           new String[] { alias });
    BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
           

总结4:通过分析可知:我们最终是向Spring中注册了一个bean,bean的名称就是类或接口的名称(也就是本例中的FeignService),bean的实现类是FeignClientFactoryBean,其属性设置就是我们在@FeignClient中定义的属性

================================================================================

总结:

  • @EnableFeignClients注解将类FeignClientsRegistrar注册到Spring中
  • FeignClientsRegistrar类主要是扫描包路径下的所有类,将带有@FeignClient注解的类或接口注册到Spring中
  • 如何注册带有@FeignClient的类或接口呢?就是生成一个BeanDefinitionHolder类,beanName为@FeignClient所在接口的名称,beanDefinition为FeignClientFactoryBean,并将@FeignClient的属性添加到FeignClientFactoryBean中
  • 至此,我们已经把带有@FeignClient注解的类或接口注册到Spring中

5.FeignClientFactoryBean源码结构分析

    可知,其实现了FactoryBean接口,那么当从ApplicationContext中获取该bean的时候,实际调用的是其getObject()方法

class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
		ApplicationContextAware{
            
	public Object getObject() throws Exception {
        // 1.获取容器中的FeignContext实现
        // 默认实现在FeignAutoConfiguration类中
		FeignContext context = applicationContext.getBean(FeignContext.class);
        // 2.主要使用构造者模式来构建一个Feign
        // 在1)中详细分析
		Feign.Builder builder = feign(context);
 
        // 3.本例中没有指定URL,故执行if
		if (!StringUtils.hasText(this.url)) {
			String url;
			if (!this.name.startsWith("http")) {
				url = "http://" + this.name;
			}
			else {
				url = this.name;
			}
			url += cleanPath();
            // 4.关键方法
            // 在2)中详细分析
			return loadBalance(builder, context, new HardCodedTarget<>(this.type,
					this.name, 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 lod 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 targeter.target(this, builder, context, new HardCodedTarget<>(
				this.type, this.name, url));
	}
}
           

5.1 Feign(context)构造者模式构建Feign.Builder

protected Feign.Builder feign(FeignContext context) {
    FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
    Logger logger = loggerFactory.create(this.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
 
    // 下面的几项getOptional()主要功能就是从ApplicationContext中获取对应类的实现
    // 这些类用户可自定义,默认的话是从FeignAutoConfiguration中获取对应bean
    Logger.Level level = getOptional(context, Logger.Level.class);
    if (level != null) {
        builder.logLevel(level);
    }
    Retryer retryer = getOptional(context, Retryer.class);
    if (retryer != null) {
        builder.retryer(retryer);
    }
    ErrorDecoder errorDecoder = getOptional(context, ErrorDecoder.class);
    if (errorDecoder != null) {
        builder.errorDecoder(errorDecoder);
    }
    Request.Options options = getOptional(context, Request.Options.class);
    if (options != null) {
        builder.options(options);
    }
    Map<String, RequestInterceptor> requestInterceptors = context.getInstances(
        this.name, RequestInterceptor.class);
    if (requestInterceptors != null) {
        builder.requestInterceptors(requestInterceptors.values());
    }
 
    if (decode404) {
        builder.decode404();
    }
 
    return builder;
}
           

5.2 loadBalance(builder, context, new HardCodedTarget<>(this.type,this.name, url))获取负载均衡后的对象

protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
                            HardCodedTarget<T> target) {
    // 1.获取Client的实现类,默认为LoadBalancerFeignClient类
    // 实现在FeignRibbonClientAutoConfiguration中
    Client client = getOptional(context, Client.class);
    if (client != null) {
        // 2.将LoadBalancerFeignClient包装到Feign.Builder
        builder.client(client);
        
        // 3.获取ApplicationContext中的Targeter实现
        // 默认实现为HystrixTargeter,实现在FeignAutoConfiguration类中
        Targeter targeter = get(context, Targeter.class);
        
        // 4.重点在这里
        // 我们来看下这个方法
        return targeter.target(this, builder, context, target);
    }
    ...
}
 
// HystrixTargeter.target()
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
                    Target.HardCodedTarget<T> target) {
    // Feign.Builder为feign包路径下,不是 feign.hystrix.HystrixFeign.Builder,故直接走return方法
    if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
        // 我们来看下这个方法,在feign.Feign.Builder类中
        return feign.target(target);
    }
    ...
}
 
// feign.Feign.Builder.target(target)
public <T> T target(Target<T> target) {
    return build().newInstance(target);
}
 
// feign.Feign.Builder.build()
public Feign build() {
    // 注意这个工厂类,下面还是会用到的
    SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
        new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
                                             logLevel, decode404);
    ParseHandlersByName handlersByName =
        new ParseHandlersByName(contract, options, encoder, decoder,
                                errorDecoder, synchronousMethodHandlerFactory);
    // 在这里可以看到真正返回的是ReflectiveFeign
    return new ReflectiveFeign(handlersByName, invocationHandlerFactory);
}
 
// ReflectiveFeign.newInstance(target)
public <T> T newInstance(Target<T> target) {
    // 1.分析出具体方法和对应的Handler处理,
    Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
    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)));
        }
    }
    // 2.由factory创建一个InvocationHandler,实现为FeignInvocationHandler
    // 可知其代理的target为HardCodedTarget
    InvocationHandler handler = factory.create(target, methodToHandler);
    // 3.可以看到最终返回的是一个代理
    T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[]{target.type()}, handler);
 
    for(DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
        defaultMethodHandler.bindTo(proxy);
    }
    return proxy;
}
           

总结:由以上分析可知,FeignClientFactoryBean.getObject()具体返回的是一个代理类,具体为FeignInvocationHandler

6.FeignInvocationHandler接收请求方法过程分析

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if ("equals".equals(method.getName())) {
        try {
            Object
                otherHandler =
                args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
            return equals(otherHandler);
        } catch (IllegalArgumentException e) {
            return false;
        }
    } else if ("hashCode".equals(method.getName())) {
        return hashCode();
    } else if ("toString".equals(method.getName())) {
        return toString();
    }
    // 非Object方法,则默认执行该句
    // dispatch为map,方法的实现类为SynchronousMethodHandler
    // 我们来分析SynchronousMethodHandler.invoke()方法
    return dispatch.get(method).invoke(args);
}
           

6.1 SynchronousMethodHandler.invoke()

public Object invoke(Object[] argv) throws Throwable {
    // 1.根据请求参数创建一个feign.RequestTemplate
    RequestTemplate template = buildTemplateFromArgs.create(argv);
    // 2.用户定义的重试策略
    Retryer retryer = this.retryer.clone();
    while (true) {
        try {
            // 重要方法在这里
            return executeAndDecode(template);
        } catch (RetryableException e) {
            retryer.continueOrPropagate(e);
            if (logLevel != Logger.Level.NONE) {
                logger.logRetry(metadata.configKey(), logLevel);
            }
            continue;
        }
    }
}
           

6.2 executeAndDecode(template)执行请求

Object executeAndDecode(RequestTemplate template) throws Throwable {
    // 1.封装请求信息,feign.Request,会将请求封装为以下信息
    // GET http://part-1-sms-interface/sms/test HTTP/1.1
    Request request = targetRequest(template);
 
    if (logLevel != Logger.Level.NONE) {
        logger.logRequest(metadata.configKey(), logLevel, request);
    }
 
    Response response;
    long start = System.nanoTime();
    try {
        // 2.真正的执行在这里
        // client为LoadBalancerFeignClient
        // 继续在3)中详细分析
        response = client.execute(request, options);
        // ensure the request is set. TODO: remove in Feign 10
        response.toBuilder().request(request).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);
 
    // 响应处理
    ...
}
           

6.3 LoadBalancerFeignClient.execute(request, options)请求负载均衡

public Response execute(Request request, Request.Options options) throws IOException {
    try {
        // 1.获取URI
        URI asUri = URI.create(request.url());
        String clientName = asUri.getHost();
        URI uriWithoutHost = cleanUrl(request.url(), clientName);
        
        // 2.封装成RibbonRequest请求
        FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(
            this.delegate, request, uriWithoutHost);
 
        // 3.封装请求参数信息
        IClientConfig requestConfig = getClientConfig(options, clientName);
        
        // 4.执行请求,并进行负载均衡
        // 本方法可分为三步:
        // 1)lbClient(clientName)获取执行类,本例中为FeignLoadBalancer
        // 2)FeignLoadBalancer.executeWithLoadBalancer()执行请求
        // 3)toResponse()获取响应
        return lbClient(clientName).executeWithLoadBalancer(ribbonRequest,
                                                            requestConfig).toResponse();
    }
    catch (ClientException e) {
        IOException io = findIOException(e);
        if (io != null) {
            throw io;
        }
        throw new RuntimeException(e);
    }
}
           

6.4 FeignLoadBalancer.executeWithLoadBalancer()执行请求

public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {
    RequestSpecificRetryHandler handler = getRequestSpecificRetryHandler(request, requestConfig);
    LoadBalancerCommand<T> command = LoadBalancerCommand.<T>builder()
        .withLoadBalancerContext(this)
        .withRetryHandler(handler)
        .withLoadBalancerURI(request.getUri())
        .build();
 
    try {
        // 在这里可以看到Hystrix的相关代码,
        return command.submit(
            new ServerOperation<T>() {
                @Override
                public Observable<T> call(Server server) {
                    URI finalUri = reconstructURIWithServer(server, request.getUri());
                    S requestForServer = (S) request.replaceUri(finalUri);
                    try {
                        // 执行ribbon负载均衡请求
                        return Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig));
                    } 
                    catch (Exception e) {
                        return Observable.error(e);
                    }
                }
            })
            .toBlocking()
            .single();
    } catch (Exception e) {
        Throwable t = e.getCause();
        if (t instanceof ClientException) {
            throw (ClientException) t;
        } else {
            throw new ClientException(e);
        }
    }
 
}
           

总结:

  • @EnableFeignClients注解将所有带有@FeignClient的类或接口注册到Spring中,注册类为FeignClientFactoryBean
  •  FeignClientFactoryBean.getObject()方法返回的是一个代理类,InvocationHandler中包含类中每个方法对应的MethodHandler,也就是SynchronousMethodHandler,方法真正执行就是SynchronousMethodHandler.invoke()方法
  •  LoadBalancerFeignClient.execute()方法进行业务的处理,在这一步操作中就用到了ribbon和Hystrix功能

调用栈大约如下 :

1. ReflectiveFeign 被反射实例化 

2. 调用ReflectiveFeign.invoke 

3. 调用SynchronousMethodHandler.invoke。此处实例化RequestTemplate 

4. 调用SynchronousMethodHandler.executeAndDecode 

5. 将RequestTemplate build为request,调用http客户端执行 

6. 将Response Decode为Object并返回

继续阅读