openfeign02-源码阅读简单总结

OpenFeign使用主要是两个部分:

第一个部分:@FeignClient注解的类及类配置信息加入进beanFactory

扫描路径,加载注解类到bean工厂,并对bean进行配置

  • 1.FeignClient注册入口
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
  • 2.获取扫描路径,并根据@FeignClient注解进行扫描,获取定义的Feignbean对象
public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
		LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();
		Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
		//指定client类的情况下
        final Class<?>[] clients = attrs == null ? null : (Class<?>[]) attrs.get("clients");
        //不指定client类的情况下,通过扫描导入
		if (clients == null || clients.length == 0) {
			ClassPathScanningCandidateComponentProvider scanner = getScanner();
			scanner.setResourceLoader(this.resourceLoader);
            //导入包含FeignClient注解的类
			scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
			//扫描路径,获取定义的bean
            Set<String> basePackages = getBasePackages(metadata);
			for (String basePackage : basePackages) {
				candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
			}
		}
		else {
			for (Class<?> clazz : clients) {
				candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz));
			}
		}

		for (BeanDefinition candidateComponent : candidateComponents) {
			if (candidateComponent instanceof AnnotatedBeanDefinition beanDefinition) {
				// verify annotated class is an interface
				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);
				String className = annotationMetadata.getClassName();
				registerClientConfiguration(registry, name, className, attributes.get("configuration"));

				registerFeignClient(registry, annotationMetadata, attributes);
			}
		}
	}

  • 3.注册FeignClient的bean到FeignClientFactoryBean工厂中,并给bean增加注解中配置的各个属性

给创建的FeignClient的bean对象增加url,path,name等配置的属性

	private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata,
			Map<String, Object> attributes) {
		String className = annotationMetadata.getClassName();
        //主要是根据属性懒解析的配置,进行两种不同的逻辑处理,出路过程大致相同
		if (String.valueOf(false).equals(
				environment.getProperty("spring.cloud.openfeign.lazy-attributes-resolution", String.valueOf(false)))) {
			eagerlyRegisterFeignClientBeanDefinition(className, attributes, registry);
		}
		else {
			lazilyRegisterFeignClientBeanDefinition(className, attributes, registry);
		}
	}

第二部分:通过FeignBeanFactory获取对应的Feign的Proxy代理类

这一块内容的核心点是从FeignClientFactoryBean类的getObject方法开始,获取Feign接口的代理类
org.springframework.cloud.openfeign.FeignClientFactoryBean#getObject

	<T> T getTarget() {
		FeignClientFactory feignClientFactory = beanFactory != null ? beanFactory.getBean(FeignClientFactory.class)
				: applicationContext.getBean(FeignClientFactory.class);
        //创建Feign的建造器,Feign的配置相关的都是在这个方法中完成的
		Feign.Builder builder = feign(feignClientFactory);
        //如果通过注册中心转发请求的时候的处理
		if (!StringUtils.hasText(url) && !isUrlAvailableInConfig(contextId)) {
			if (!name.startsWith("http://") && !name.startsWith("https://")) {
				url = "http://" + name;
			}
			else {
				url = name;
			}
			url += cleanPath();
			return (T) loadBalance(builder, feignClientFactory, new HardCodedTarget<>(type, name, url));
		}
		if (StringUtils.hasText(url) && !url.startsWith("http://") && !url.startsWith("https://")) {
			url = "http://" + url;
		}
        // 获取请求Client,有几种形式
		Client client = getOptional(feignClientFactory, Client.class);
		if (client != null) {
			if (client instanceof FeignBlockingLoadBalancerClient) {
				client = ((FeignBlockingLoadBalancerClient) client).getDelegate();
			}
			if (client instanceof RetryableFeignBlockingLoadBalancerClient) {
				client = ((RetryableFeignBlockingLoadBalancerClient) client).getDelegate();
			}
			builder.client(client);
		}

		applyBuildCustomizers(feignClientFactory, builder);

		Targeter targeter = get(feignClientFactory, Targeter.class);
        //这里就是创建实例的部分,具体在第三部分进行分析
		return targeter.target(this, builder, feignClientFactory, resolveTarget(feignClientFactory, contextId, url));
	}

第三部分:代理类执行方法调用接口

如何将Feign配置的路径等组装,远程调用

feign.ReflectiveFeign.FeignInvocationHandler就是Feign代理类调用程序的实现类

 public <T> T newInstance(Target<T> target, C requestContext) {
        ********************* 省略
        //这里获取目标类的方法执行类
        Map<Method, InvocationHandlerFactory.MethodHandler> methodToHandler = this.targetToHandlersByName.apply(target, requestContext);
        //这里的获取代理类的执行类,也就是FeignInvocationHandler
        InvocationHandler handler = this.factory.create(target, methodToHandler);
        T proxy = Proxy.newProxyInstance(target.type().getClassLoader(), new Class[]{target.type()}, handler);
        ********************** 省略
        return proxy;
    }

获取调用方法的Handler,组装请求及调用接口

  • 1.获取MethodHandler
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
   if (!"equals".equals(method.getName())) {
        ******************************** 省略
        else {
            //这里是MethodHandler调用方法,也是执行的核心逻辑部分
            return ((InvocationHandlerFactory.MethodHandler)this.dispatch.get(method)).invoke(args);
        }
    } else {
        *************************** 省略
    }
}

  • 2.组装请求实体及调用,以同步调用代码为例
public Object invoke(Object[] argv) throws Throwable {
    //创建请求Template
    RequestTemplate template = this.buildTemplateFromArgs.create(argv);
    //获取请求Options
    Request.Options options = this.findOptions(argv);
    //请求调用
    while(true) {
        try {
            return this.executeAndDecode(template, options);
        } catch (RetryableException var9) {
          // 代码重试
          **********************省略
        }
    }
}

  • 3.提供了三种发送远程请求的方式
    • 负载均衡,默认的,还有同步发送多个请求的类型
  public Response execute(Request request, Request.Options options) throws IOException {
      HttpURLConnection connection = this.convertAndSend(request, options);
      return this.convertResponse(connection, request);
  }

总结:OpenFeign的内部逻辑还是挺多的,后续再根据阅读情况进一步补充

posted @   PerfectLi  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· 葡萄城 AI 搜索升级:DeepSeek 加持,客户体验更智能
· 什么是nginx的强缓存和协商缓存
· 一文读懂知识蒸馏
点击右上角即可分享
微信分享提示