【Feign】创建源码解析

302 阅读3分钟

这是我参与更文挑战的第19天,活动详情查看:更文挑战

一、前言

OpenFeign 声明的接口不论是 SpringMVC 还是 JAX-RS,其底层都会把接口解析成方法元数据(MethodMetadata),再通过动态代理生成接口的代理,并基于 MethodMetadata 进行 Rest 调用。

背景:在 Spring Cloud 集成中。


可概括为:1-3步是创建 Feign ,4-5步是请求处理机制。

Feign 通过包扫描注入 FeignClient 的 Bean,该源码在 FeignClientsRegistrar 类中:

  1. 程序启动时,会检查是否有 @EnableFeignClients 注解,如果有该注解,则开启包扫描,扫描被 @FeignClient 注解的接口。

  2. 当程序的启动类上有 @EnableFeignClients 注解。在程序启动后,程序会通过包扫描将有 @FeignClient 注解修饰的接口连同接口名和注解的信息一起取出,赋给 BeanDefinitionBuilder ,然后根据 BeanDefinitionBuilder 得到 BeanDefinition,最后将 BeanDefinition 注入 IOC 容器中。

  3. 注入 BeanDefinition 之后,通过 JDK 的代理,当调用 Feign Client 接口里面的方法时,该方法会被拦截,源码在 ReflectiveFeign 类。

  4. 在 SynchronousMethodHandler 类进行拦截处理,会根据参数生成 RequestTemplate 对象,该对象是 HTTP 请求的模板

  5. executeAndDecode() 方法,该方法会通过 RequestTemplate 生成 Request 请求对象,然后用 Http Client 获取 Response,即通过 Http Client 进行 Http 请求来获取响应



二、直接怼源码

发现之前看源码,是画类似流程图,但感觉不是特别好,也不方便之后回顾,这次试试时序图。

创建 Feign 时序图,如下:

feign创建时序图.png

步骤:

  1. @EnableFeignClients 注解提供的包名与 @FeignClient 注解修饰的接口找到所有的接口
  2. 基于这些接口构造 FeignClientFactoryBean 这个 FactoryBean
  3. FactoryBean 内部真正构造的对象是一个 Proxy,这个 Proxy 是通过 Targeter#target 构造出来的。Targeter 内部构造通过 Feign.Builder#build 方法完成,build 方法返回的一个 Feign 对象。

先从 @EnableFeignClients 入手:

// 定位:org.springframework.cloud.openfeign
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
// 重要:导入 Bean
// 那么这个 FeignClientsRegistrar 肯定是处理注册的
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
    ... ...
}

再看 FeignClientsRegistrar :

// 定位:org.springframework.cloud.openfeign
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
    
    // Spring 会注入参数,并调用此方法
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata,
                                        BeanDefinitionRegistry registry) {
        // 1. 注册默认配置
        registerDefaultConfiguration(metadata, registry);
        // 2. 扫描 @FeignClient 并注册
        registerFeignClients(metadata, registry);
    }
    
    // 1. 注册默认配置
    private void registerDefaultConfiguration(AnnotationMetadata metadata,
                                              BeanDefinitionRegistry registry) {
        // 获取 @EnableFeignClients 这个注解的所有属性的值
        Map<String, Object> defaultAttrs = metadata
            .getAnnotationAttributes(EnableFeignClients.class.getName(), true);

        if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
            // 获取 Application 启动类的全限定名:
            // default.cn.donald.dolphin.oceanfile.OceanFileApplication
            // ... 
            
            // 在 BeanDefinitionRegistry 中注册
            registerClientConfiguration(registry, name,
                                        defaultAttrs.get("defaultConfiguration"));
        }
    }
    // 2. 扫描 @FeignClient 并注册
    public void registerFeignClients(AnnotationMetadata metadata,
			BeanDefinitionRegistry registry) {
        // 2.1 获取 scanner:用于在指定的路径中,扫描指定条件相关的 Bean 类
		ClassPathScanningCandidateComponentProvider scanner = getScanner();
		scanner.setResourceLoader(this.resourceLoader);

        // 2.2
		// ... 一大坨代码:用于扫描指定包路径,此时会有 filter 对指定过滤

        // 2.3 处理包路径
		for (String basePackage : basePackages) {
            // 这是扫描出来的候选 BeanDeifinition,即包含了@FeignClient注解的接口
			Set<BeanDefinition> candidateComponents = scanner
					.findCandidateComponents(basePackage);
			for (BeanDefinition candidateComponent : candidateComponents) {
				if (candidateComponent instanceof AnnotatedBeanDefinition) {
					// 2.3.1 校验是否是接口 (interface)
                    // 2.3.2 获取 @FeignClient 中的属性
                    // ...
                    
                    // 2.3.3 注册配置
                    registerClientConfiguration(registry, name,
							attributes.get("configuration"));
                    // 2.3.4 注册这个 feignClient
					registerFeignClient(registry, annotationMetadata, attributes);
				}
			}
		}
	}
    
    // 2.3.4 注册这个 feignClient
    private void registerFeignClient(BeanDefinitionRegistry registry,
			AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
        
        // 重要:
        BeanDefinitionBuilder definition = BeanDefinitionBuilder
				.genericBeanDefinition(FeignClientFactoryBean.class);
        
        // ...  一坨配置设置
        // ... 注册进 Spring 容器中
    }
}

来看下 FeignClientFactoryBean :

// 定位:org.springframework.cloud.openfeign
class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {
    // 重载方法,入口就在此:
    @Override
	public Object getObject() throws Exception {
		return getTarget();
	}
    
    <T> T getTarget() {
		FeignContext context = applicationContext.getBean(FeignContext.class);
		Feign.Builder builder = feign(context);

		// ... 一坨 URL,http 处理
        
		// target 明显动态代理:
        // 先获取实例 instance
		Targeter targeter = get(context, Targeter.class);
        // 再生成对应
		return (T) targeter.target(this, builder, context, new HardCodedTarget<>(
				this.type, this.name, url));
	}
}

targeter.target() 默认走 DefaultTargeter:

// 定位:org.springframework.cloud.openfeign
class DefaultTargeter implements Targeter {

	@Override
	public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, 
                        FeignContext context,
						Target.HardCodedTarget<T> target) {
		return feign.target(target);
	}
}

// 进入 feign 类
// 定位:feign
public abstract class Feign {
    // 1
    public <T> T target(Target<T> target) {
      return build().newInstance(target);
    }
    // 2. 走子类的 ReflectiveFeign
    public abstract <T> T newInstance(Target<T> target);
}

// 默认情况下返回的是 `ReflectiveFeign` 这个 `Feign` 对象的子类
// 进入 ReflectiveFeign 类
// 定位:feign
public class ReflectiveFeign extends Feign {
    @Override
    public <T> T newInstance(Target<T> target) {
        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)));
            }
        }
        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;
    }
}

从这段代码可以看到,InvocationHandler、Proxy 这些 JDK 内置的动态代理类完成了这个操作。