【Feign】创建源码解析

218 阅读3分钟

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

一、前言

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

背景:在 Spring Cloud 集成中。


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

Feign 通过包扫描注入 FeignClientBean,该源码在 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;
    }
}

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