spring cloud openfeign 的加载创建

前面说到ImportBeanDefinitionRegistrar 接口 这个接口 在spring cloud openfeign 中也在使用,@EnableFeignClients 注解中就有使用 具体不在叙述

解读一下加载过程
@EnableFeignClients 通过这个注解可以知道加载是通过FeignClientsRegistrar类进行
FeignClientsRegistrar.java


	@Override
	public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
                //注册默认的openfeign接口
		registerDefaultConfiguration(metadata, registry);
                //注册所有带有@FeignClient注解的接口
		registerFeignClients(metadata, registry);
	}

查看registerDefaultConfiguration()方法 该方法主要是获取@EnableFeignClients 注解中的defaultConfiguration字段对应的配置类,
并且将这个类绑定到FeignClientSpecification类中 一起注册到容器中

	private void registerDefaultConfiguration(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
                //获取EnableFeignClients注解上的信息 
		Map<String, Object> defaultAttrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName(), true);
                //判断是否有defaultConfiguration这个字段
		if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
			String name;
                        // 是否有配置 没有配置那么就是EnableFeignClients所在的类
			if (metadata.hasEnclosingClass()) {
				name = "default." + metadata.getEnclosingClassName();
			}
			else {
				name = "default." + metadata.getClassName();
			}
                        // 注册client的配置信息
			registerClientConfiguration(registry, name, defaultAttrs.get("defaultConfiguration"));
		}
	}

        //注册client的配置信息
	private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name, Object configuration) {
                //生成BeanDefinition 
		BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FeignClientSpecification.class);
		builder.addConstructorArgValue(name);
		builder.addConstructorArgValue(configuration);
                //注册FeignClientSpecification的BeanDefinition
		registry.registerBeanDefinition(name + "." + FeignClientSpecification.class.getSimpleName(),
				builder.getBeanDefinition());
	}

接下来看一下 如何注册 feign 接口的 注册接口在registerFeignClients()方法中 主要分为下面几个步骤

  1. 获取EnableFeignClients注解的信息
  2. 从注解信息中获取出basePackage路径,或者 clients中配置的feign接口类信息
  3. 通过basePackage路径扫描出下面所有带有@FeignClient注解的类信息
  4. 注册beanDefinition
public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
		LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();
                //获取注解信息
		Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
                //获取是否在注解上配置clients类信息
		final Class<?>[] clients = attrs == null ? null : (Class<?>[]) attrs.get("clients");
		if (clients == null || clients.length == 0) {
			ClassPathScanningCandidateComponentProvider scanner = getScanner();
			scanner.setResourceLoader(this.resourceLoader);
			scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
			Set<String> basePackages = getBasePackages(metadata);
			for (String basePackage : basePackages) {
                                //扫描路径下所有带有FeignClient
				candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
			}
		}
		else {
			for (Class<?> clazz : clients) {
				candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz));
			}
		}

		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);
				registerClientConfiguration(registry, name, attributes.get("configuration"));
                                //注册FeignClient
				registerFeignClient(registry, annotationMetadata, attributes);
			}
		}
	}

上面看了spring cloud openfeign 是如何扫描 加载openfeign 接口的 接下来看看 如何生成openfeign的代理类
在registerFeignClient()方法中注册 具体逻辑

  1. 创建一个FeignClientFactoryBean对象
  2. 为这个factoryBean添加一些属性 比如FeignClient注解中的一些配置
  3. 创建BeanDefinitionBuilder 这个BeanDefinition的类型实际上是由FeignClientFactoryBean.getObject()方法进行代理类创建
  4. 最终将BeanDefinition注册到容器中
	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 factoryBean = new FeignClientFactoryBean();
		factoryBean.setBeanFactory(beanFactory);
		factoryBean.setName(name);
		factoryBean.setContextId(contextId);
		factoryBean.setType(clazz);
		factoryBean.setRefreshableClient(isClientRefreshEnabled());
		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);
		BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);

		registerOptionsBeanDefinition(registry, contextId);
	}

FeignClientFactoryBean 生成代理类逻辑 众所周知FactoryBean可以用来生成实例bean对象 查看FeignClientFactoryBean类的getTarget()方法 具体逻辑

  1. 从FeignContext中获取Encoder,Decoder,Contract。。。等内容 并生成一个Feign.Builder 对象该类是属于feign.core依赖中的 不属于openfeign
  2. 为Builder对象设置属性如 url client,
  3. 最后生成feign 的代理对象

我们调用自己定义的feign 接口类 实际上是在调用生成的feign代理对象

	<T> T getTarget() {
		FeignContext context = beanFactory != null ? beanFactory.getBean(FeignContext.class)
				: applicationContext.getBean(FeignContext.class);
		Feign.Builder builder = feign(context);

		if (!StringUtils.hasText(url)) {

			if (LOG.isInfoEnabled()) {
				LOG.info("For '" + name + "' URL not provided. Will try picking an instance via load-balancing.");
			}
			if (!name.startsWith("http")) {
				url = "http://" + name;
			}
			else {
				url = name;
			}
			url += cleanPath();
			return (T) loadBalance(builder, context, new HardCodedTarget<>(type, name, url));
		}
		if (StringUtils.hasText(url) && !url.startsWith("http")) {
			url = "http://" + url;
		}
		String url = this.url + cleanPath();
		Client client = getOptional(context, Client.class);
		if (client != null) {
			if (client instanceof FeignBlockingLoadBalancerClient) {
				// not load balancing because we have a url,
				// but Spring Cloud LoadBalancer is on the classpath, so unwrap
				client = ((FeignBlockingLoadBalancerClient) client).getDelegate();
			}
			if (client instanceof RetryableFeignBlockingLoadBalancerClient) {
				// not load balancing because we have a url,
				// but Spring Cloud LoadBalancer is on the classpath, so unwrap
				client = ((RetryableFeignBlockingLoadBalancerClient) client).getDelegate();
			}
			builder.client(client);
		}
		Targeter targeter = get(context, Targeter.class);
		return (T) targeter.target(this, builder, context, new HardCodedTarget<>(type, name, url));
	}

posted @ 2022-12-07 15:29  yangqifang  阅读(229)  评论(0编辑  收藏  举报