栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

spring cloud——openFegin 组件学习

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

spring cloud——openFegin 组件学习

说明:学习代码使用的是ruoyi-cloud开源代码

一、使用

在接口上写入一个@FeignClient 注解

@FeignClient(contextId = "remoteFileService", value = ServiceNameConstants.FILE_SERVICE, fallbackFactory = RemoteFileFallbackFactory.class)
public interface RemoteFileService
{
    
    @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public R upload(@RequestPart(value = "file") MultipartFile file);
}

FeignClient 注解属性,这个博客写过

二、Feign 如何实现

首先是使用@EnableFeignClients注解导入一个FeignClientsRegistrar.class,因为这个类实现了importBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware。Aware接口啥的都是set一些属性,就不看了。直接看importBeanDefinitionRegistrar的实现方法

@Override
	public void registerBeanDefinitions(Annotationmetadata metadata, BeanDefinitionRegistry registry) {
		// 注册默认的配置,可以参考FeignClient 注解属性中的configuration属性,只不过这个是加载EnableFeignClients中的defaultConfiguration属性
		registerDefaultConfiguration(metadata, registry);
		// 注册客户端
		registerFeignClients(metadata, registry);
	}
    注册配置类的registerDefaultConfiguration代码
private void registerDefaultConfiguration(Annotationmetadata metadata, BeanDefinitionRegistry registry) {
		// 获取EnableFeignClients注解中的所有属性
		Map defaultAttrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName(), true);

		if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
			String name;
			// 是否是内部类,是的话,就去取内部类的名字 xxx.xxx.xx.Class$Neibu
			if (metadata.hasEnclosingClass()) {
				name = "default." + metadata.getEnclosingClassName();
			}
			else {
				name = "default." + metadata.getClassName();
			}
			// 将这些类注册为 FeignClientSpecification.class
			// bean 的名字为 default.xxx.xxx.xx.Class$Neibu.FeignClientSpecification
			registerClientConfiguration(registry, name, defaultAttrs.get("defaultConfiguration"));
		}
	}

因为上面注册成了一个FeignClientSpecification类嘛,这个类是实现了NamedContextFactory.Specification,但是NamedContextFactory中的代码有很多

1、注册client

public void registerFeignClients(Annotationmetadata metadata, BeanDefinitionRegistry registry) {

		linkedHashSet candidateComponents = new linkedHashSet<>();
		Map attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
		final Class[] clients = attrs == null ? null : (Class[]) attrs.get("clients");
		// 如果指定了feginClient 那么就直接用指定的,如果没指定就去扫描包,
		if (clients == null || clients.length == 0) {
			ClassPathScanningCandidateComponentProvider scanner = getScanner();
			scanner.setResourceLoader(this.resourceLoader);
			// 包含FeignClient注解的文件
			scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
			Set 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) {
				// 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 attributes = annotationmetadata
						.getAnnotationAttributes(FeignClient.class.getCanonicalName());

				String name = getClientName(attributes);
				// 注册FeignClient注解中configuration的配置类
				registerClientConfiguration(registry, name, attributes.get("configuration"));
				// 注册FeignClient客户端
				registerFeignClient(registry, annotationmetadata, attributes);
			}
		}
	}
具体的注册FeignClient,主要是将这个接口类注册为FeignClientFactoryBean以及设置一些属性信息。
private void registerFeignClient(BeanDefinitionRegistry registry, Annotationmetadata annotationmetadata,
			Map 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, 以及InitializingBean,还有俩Aware接口,直接看getObject() 方法 和 afterPropertiesSet()方法

public class FeignClientFactoryBean
		implements FactoryBean, InitializingBean, ApplicationContextAware, BeanFactoryAware
 

验证你FeginClient的 contextId 和 name属性 有没有值

public void afterPropertiesSet() {
		Assert.hasText(contextId, "Context id must be set");
		Assert.hasText(name, "Name must be set");
	}
@Override
	public Object getObject() {
		return getTarget();
	}

	
	 T getTarget() {
		FeignContext context = beanFactory != null ? beanFactory.getBean(FeignContext.class)
				: applicationContext.getBean(FeignContext.class);
				// 个人理解这行代码就是把你原先的日志啊、编解码器啊等东西设置进去,获取一个builder对象。说白了就是构造器模式创建嘛
		Feign.Builder builder = feign(context);
		// 没指定url的话,就用http:// + 服务名称
		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));
	}

注册Client流程图

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号