SpringCloud之Feign原理与源码分析

Feign原理 - 源码解析

本文基于SpringCloud版本:Hoxton.SR12(2.2.9.RELEASE)

一、扫描FeignClient生成代理Bean放入Spring容器

1.@EnableFeignClient注解导入注册类。

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({FeignClientsRegistrar.class})
public @interface EnableFeignClients {
    String[] value() default {};

    String[] basePackages() default {};

    Class<?>[] basePackageClasses() default {};

    Class<?>[] defaultConfiguration() default {};

    Class<?>[] clients() default {};
}

@Import注解用于注册Bean到容器,FeignClientsRegistrar类实现ImportBeanDefinitionRegistrar接口。

ImportBeanDefinitionRegistrar接口中的registerBeanDefinitions方法负责注册Bean。

进入FeignClientsRegistrar类registerBeanDefinitions方法:

public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        this.registerDefaultConfiguration(metadata, registry);
        this.registerFeignClients(metadata, registry);
}

其中this.registerDefaultConfiguration(metadata, registry)负责注册@EnableFeignClients注解指定defaultConfiguration的配置类到容器。

进入this.registerFeignClients(metadata, registry)方法:

public void registerFeignClients(AnnotationMetadata metadata,
			BeanDefinitionRegistry registry) {
		//存放需要注册到容器bean对应的BeanDefinition
		LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();
        //@EnableFeignClients注解中指定的值
		Map<String, Object> attrs = metadata
				.getAnnotationAttributes(EnableFeignClients.class.getName());
		//@EnableFeignClients注解中指定的clients
         final Class<?>[] clients = attrs == null ? null
				: (Class<?>[]) attrs.get("clients");
        //@EnableFeignClients注解没有指定clients属性的情况下
		if (clients == null || clients.length == 0) {
            //获取候选Bean扫描器,该扫描器只扫描非注解类并且能够实例化的类
			ClassPathScanningCandidateComponentProvider scanner = getScanner();
			scanner.setResourceLoader(this.resourceLoader);
            //指定扫描带有@FeignClient注解的类
			scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
            /** 获取@EnableFeignClients注解中value、basePackages、basePackageClasses属性指定              ** 的包作为扫描路径,若value、basePackages、basePackageClasses都没有指定,则扫描路径              ** 默认为@EnableFeignClients所在包的路径**/
			Set<String> basePackages = getBasePackages(metadata);
            //用扫描器扫描指定的路径,筛选出符合注册条件的Bean
			for (String basePackage : basePackages) {
				candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
			}
		}
		else {
            //@EnableFeignClients注解指定clients属性的情况下
			for (Class<?> clazz : clients) {
				candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz));
			}
		}

		for (BeanDefinition candidateComponent : candidateComponents) {
			if (candidateComponent instanceof AnnotatedBeanDefinition) {
				//验证@FeignClient注解是否标注在接口上
				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());
				//决定Bean name
				String name = getClientName(attributes);
                //注册@Feign注解中属性configuration指定的配置类到容器中
				registerClientConfiguration(registry, name,
						attributes.get("configuration"));
				//注册@Fegin注解标注的类的容器
				registerFeignClient(registry, annotationMetadata, attributes);
			}
		}
}

进入registerFeignClient(registry, annotationMetadata, attributes)方法:

private void registerFeignClient(BeanDefinitionRegistry registry,
			AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
		String className = annotationMetadata.getClassName();
		Class clazz = ClassUtils.resolveClassName(className, null);
		ConfigurableBeanFactory beanFactory = registry instanceof ConfigurableBeanFactory
				? (ConfigurableBeanFactory) registry : null;
		String contextId = getContextId(beanFactory, attributes);
		String name = getName(attributes);
        //关键来了: 创建FeignClientFactoryBean对象
		FeignClientFactoryBean factoryBean = new FeignClientFactoryBean();
        //为创建FeignClientFactoryBean对象设置参数
		factoryBean.setBeanFactory(beanFactory);
		factoryBean.setName(name);
		factoryBean.setContextId(contextId);
		factoryBean.setType(clazz);
        //根据FeignClientFactoryBean对象构造BeanDefinition
		BeanDefinitionBuilder definition = BeanDefinitionBuilder
				.genericBeanDefinition(clazz, () -> {
					factoryBean.setUrl(getUrl(beanFactory, attributes));
					factoryBean.setPath(getPath(beanFactory, attributes));
					factoryBean.setDecode404(Boolean
							.parseBoolean(String.valueOf(attributes.get("decode404"))));
					Object fallback = attributes.get("fallback");
					if (fallback != null) {
						factoryBean.setFallback(fallback instanceof Class
								? (Class<?>) fallback
								: ClassUtils.resolveClassName(fallback.toString(), null));
					}
					Object fallbackFactory = attributes.get("fallbackFactory");
					if (fallbackFactory != null) {
						factoryBean.setFallbackFactory(fallbackFactory instanceof Class
								? (Class<?>) fallbackFactory
								: ClassUtils.resolveClassName(fallbackFactory.toString(),
										null));
					}
					return factoryBean.getObject();
				});
		definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
		definition.setLazyInit(true);
		validate(attributes);

		AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
		beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, className);
		beanDefinition.setAttribute("feignClientsRegistrarFactoryBean", factoryBean);

		// has a default, won't be null
		boolean primary = (Boolean) attributes.get("primary");

		beanDefinition.setPrimary(primary);

		String[] qualifiers = getQualifiers(attributes);
		if (ObjectUtils.isEmpty(qualifiers)) {
			qualifiers = new String[] { contextId + "FeignClient" };
		}

		BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
				qualifiers);
        //将FeignClientFactoryBean对象对应的BeanDefinition转化为Bean注册到容器
		BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
	}

在SpringIOC源码中,从Spring容器中获取Bean都是根据对应的FactoryBean接口中getObject()方法获取。

FeignClientFactoryBean实现了FactoryBean接口,即每次从Spring容器中获取FeignClient都是根据getObject方法获取。

进入FeignClientFactoryBean#getObject:

@Override
public Object getObject() {
	return getTarget();
}

一直深入内部直到ReflectiveFeign#newInstance方法:

public <T> T newInstance(Target<T> target) {
        //Feign接口中的方法与方法处理器映射
        Map<String, MethodHandler> nameToHandler = this.targetToHandlersByName.apply(target);
        Map<Method, MethodHandler> methodToHandler = new LinkedHashMap();
        List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList();
        Method[] var5 = target.type().getMethods();
        int var6 = var5.length;

        //为nameToHandler赋值
        for(int var7 = 0; var7 < var6; ++var7) {
            Method method = var5[var7];
            if (method.getDeclaringClass() != Object.class) {
                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)));
                }
            }
        }
        //生成JDK代理需要的InvocationHandler,顺便把methodToHandler存放在代理处理类,用于根据方法         //获取对应的方法处理器
        InvocationHandler handler = this.factory.create(target, methodToHandler);//创建代理对象
        T proxy = Proxy.newProxyInstance(target.type().getClassLoader(), new Class[]{target.type()}, handler);
        Iterator var12 = defaultMethodHandlers.iterator();

        while(var12.hasNext()) {
            DefaultMethodHandler defaultMethodHandler = (DefaultMethodHandler)var12.next();
            defaultMethodHandler.bindTo(proxy);
        }

        return proxy;
   }

二、Feign对象的代理逻辑

众所周知,执行代理对象的方法时实际上是执行InvocationHandler#invoke方法。

FeignInvocationHandler#invoke方法逻辑为:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if (!"equals".equals(method.getName())) {
                if ("hashCode".equals(method.getName())) {
                    return this.hashCode();
                } else {
                    //不是equals、hashCode、toString方法时,根据方法获取对应的方法处理器,并执行                       //invoke方法
                    return "toString".equals(method.getName()) ? this.toString() : ((MethodHandler)this.dispatch.get(method)).invoke(args);
                }
            } else {
                try {
                    Object otherHandler = args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
                    return this.equals(otherHandler);
                } catch (IllegalArgumentException var5) {
                    return false;
                }
            }
}

其中MethodHandler接口的实现类为SynchronousMethodHandler,其invoke方法即为调用feign接口进行远程调用时实际执行的逻辑。

三、Feign远程调用时执行的逻辑

执行流程图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NXcVLleu-1628600042181)(./feign执行流程.png)]

将断点设置在调用feign方法中,在IDEA中按F7由debug进入SynchronousMethodHandler#invoke方法:

public Object invoke(Object[] argv) throws Throwable {
        //生成RequestTemplate
        RequestTemplate template = this.buildTemplateFromArgs.create(argv);
        //获取配置参数
        Options options = this.findOptions(argv);
        Retryer retryer = this.retryer.clone();

        while(true) {
            try {
                //执行远程调用并将响应结果解码。
                return this.executeAndDecode(template, options);
            } catch (RetryableException var9) {
                RetryableException e = var9;

                try {
                    //失败了没关系,重试。
                    retryer.continueOrPropagate(e);
                } catch (RetryableException var8) {
                    Throwable cause = var8.getCause();
                    if (this.propagationPolicy == ExceptionPropagationPolicy.UNWRAP && cause != null) {
                        throw cause;
                    }

                    throw var8;
                }

                if (this.logLevel != Level.NONE) {
                    this.logger.logRetry(this.metadata.configKey(), this.logLevel);
                }
            }
        }
    }

其中RequestTemplate中已填充的参数有:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7dY3kikV-1628600042186)(./requestTemplate.png)]

获取Options参数:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ysdTawqM-1628600042188)(./options参数.png)]

进入执行远程调用并将响应结果解码方法executeAndDecode:

Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
        //执行RequestInterceptor拦截器
        Request request = this.targetRequest(template);
        if (this.logLevel != Level.NONE) {
            this.logger.logRequest(this.metadata.configKey(), this.logLevel, request);
        }

        long start = System.nanoTime();

        Response response;
        try {
            //远程调用
            response = this.client.execute(request, options);
            //设置request和template在响应结果中。
            response = response.toBuilder().request(request).requestTemplate(template).build();
        } catch (IOException var12) {
            if (this.logLevel != Level.NONE) {
                this.logger.logIOException(this.metadata.configKey(), this.logLevel, var12, this.elapsedTime(start));
            }

            throw FeignException.errorExecuting(request, var12);
        }

        long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
        if (this.decoder != null) {
            //默认使用SpringDecoder解码器,里面用到HttpMessageConvertor,即SpringMVC能转换的响应结果这里也能转换
            return this.decoder.decode(response, this.metadata.returnType());
        } else {
            CompletableFuture<Object> resultFuture = new CompletableFuture();
            this.asyncResponseHandler.handleResponse(resultFuture, this.metadata.configKey(), response, this.metadata.returnType(), elapsedTime);

            try {
                if (!resultFuture.isDone()) {
                    throw new IllegalStateException("Response handling not done");
                } else {
                    return resultFuture.join();
                }
            } catch (CompletionException var13) {
                Throwable cause = var13.getCause();
                if (cause != null) {
                    throw cause;
                } else {
                    throw var13;
                }
            }
        }
    }

其中进行远程调用的Client有:

(1)Client.Default类:默认的feign.Client 客户端实现类,内部使用HttpURLConnnection 完成URL请求处理;

在JKD1.8中,虽然在HttpURLConnnection 底层,使用了非常简单的HTTP连接池技术,但是,其HTTP连接的复用能力,实际是非常弱的,性能当然也很低。

(2)ApacheHttpClient 类:内部使用 Apache httpclient 开源组件完成URL请求处理的feign.Client 客户端实现类;

(3)OkHttpClient类:内部使用 OkHttp3 开源组件完成URL请求处理的feign.Client 客户端实现类。

(4)LoadBalancerFeignClient 类:内部使用 Ribben 负载均衡技术完成URL请求处理的feign.Client 客户端实现类。

参考文献:

1.https://www.cnblogs.com/crazymakercircle/p/11965726.html

2.https://docs.spring.io/spring-cloud-openfeign/docs/2.2.9.RELEASE/reference/html/

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值