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

Feign源码的入口

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

Feign源码的入口

Feign源码的入口

首先看一下demo工具的整体结构,ServiceB和ServiceA都要注册到eureka中,而serviceA-api是一个通用的api模块,由于Feign具有继承的特性,所以把接口都抽取出来了。

具体的代码如下:

@RequestMapping("/user")  
public interface ServiceAInterface {

	@RequestMapping(value = "/sayHello/{id}", method = RequestMethod.GET)
	String sayHello(@PathVariable("id") Long id,
                    @RequestParam("name") String name,
                    @RequestParam("age") Integer age);

	@RequestMapping(value = "/", method = RequestMethod.POST)
	String createUser(@RequestBody User user);

	@RequestMapping(value = "/{id}", method = RequestMethod.PUT)
	String updateUser(@PathVariable("id") Long id, @RequestBody User user);

	@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
	String deleteUser(@PathVariable("id") Long id);

	@RequestMapping(value = "/{id}", method = RequestMethod.GET)
	User getById(@PathVariable("id") Long id);
	
}

而在ServiceAClient中,只要对这个接口进行继承,加上注解就可以了。

@FeignClient("ServiceA")
public interface ServiceAClient extends ServiceAInterface {
 
}

还要在启动上加上开启Feign客户端的注解@EnableFeignClients,就能起作用了。那我们就点进去这个注解看看。

@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 {};

}

注解上面会注入Feign客户端的注册器,那我们就点进去看看。

importBeanDefinitionRegistrar来实现Bean的动态注入。它是Spring中一个强大的扩展接口,可以动态的往容器中,注入Bean,基本可以确定,就是实现它,来往容器中注入Feign的客户端的。

@Override
	public void registerBeanDefinitions(Annotationmetadata metadata,
			BeanDefinitionRegistry registry) {
		registerDefaultConfiguration(metadata, registry);
		registerFeignClients(metadata, registry);
	}

首先先看一下第一个方法,名字注册一些默认的配置信息,点进去看看。大概的意思就是获取到EnableFeignClients这个注解上的一些信息,如果信息不为空,且包含模型配置的leu,就进入下面的判断。此方法不是核心的方法,主要留意一下registerFeignClients方法。

private void registerDefaultConfiguration(Annotationmetadata metadata,
			BeanDefinitionRegistry registry) {
		Map defaultAttrs = metadata
				.getAnnotationAttributes(EnableFeignClients.class.getName(), true);

		if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
			String name;
			if (metadata.hasEnclosingClass()) {
				name = "default." + metadata.getEnclosingClassName();
			}
			else {
				name = "default." + metadata.getClassName();
			}
			registerClientConfiguration(registry, name,
					defaultAttrs.get("defaultConfiguration"));
		}
	}

那就点到registerFeignClients方法看看。

public void registerFeignClients(Annotationmetadata metadata,
			BeanDefinitionRegistry registry) {
		ClassPathScanningCandidateComponentProvider scanner = getScanner();
		scanner.setResourceLoader(this.resourceLoader);

		Set basePackages;
                //拿到注解的一些信息
		Map attrs = metadata
				.getAnnotationAttributes(EnableFeignClients.class.getName());
                //设置注解类型的过滤器,看上去是为了获取标记FeignClient这个注解类
		AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
				FeignClient.class);
		final Class[] clients = attrs == null ? null
				: (Class[]) attrs.get("clients");
        
                //由于@EnableFeignClients注解中来配置clients属性,一般不会填,所以可以clients默认一般都是空的
		if (clients == null || clients.length == 0) {
			scanner.addIncludeFilter(annotationTypeFilter);
                       //getbasePackages这里肯定获取@EnableFeignClients这个注解的包的路径
			basePackages = getbasePackages(metadata);
		}
		else {
			final Set clientClasses = new HashSet<>();
			basePackages = new HashSet<>();
			for (Class clazz : clients) {
				basePackages.add(ClassUtils.getPackageName(clazz));
				clientClasses.add(clazz.getCanonicalName());
			}
			AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {
				@Override
				protected boolean match(Classmetadata metadata) {
					String cleaned = metadata.getClassName().replaceAll("\$", ".");
					return clientClasses.contains(cleaned);
				}
			};
			scanner.addIncludeFilter(
					new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));
		}

	for (String basePackage : basePackages) {
            //1.这步的话就是根据包路径,去找到这个包下面有哪些FeignClient注解的类
			Set candidateComponents = scanner
					.findCandidateComponents(basePackage);
			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);
					registerClientConfiguration(registry, name,
							attributes.get("configuration"));

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

下面这个方法至关重要,那我们就点进去看看

Set candidateComponents = scanner
					.findCandidateComponents(basePackage);

下面就来到scanCandidateComponents方法中

private Set scanCandidateComponents(String basePackage) {
		Set candidates = new linkedHashSet<>();
		try {
                       //此处就是包的路径加上*.class
			String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
					resolvebasePackage(basePackage) + '/' + this.resourcePattern;
                       //根据上面得到处理的路径,去拿到包路径下面所有的class文件,转成一个个Resource
			Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
			boolean traceEnabled = logger.isTraceEnabled();
			boolean debugEnabled = logger.isDebugEnabled();
            
                        //遍历所有的资源
			for (Resource resource : resources) {
				if (traceEnabled) {
					logger.trace("Scanning " + resource);
				}
				if (resource.isReadable()) {
					try {
						metadataReader metadataReader = getmetadataReaderFactory().getmetadataReader(resource);
                                                 //此处方法最为重要,由于前面创建的scanner。重写了isCandidateComponent方法,这里就是从所有的class中
                                              //   获取所有有的Feign的客户端
						if (isCandidateComponent(metadataReader)) {
							ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
							sbd.setSource(resource);
							if (isCandidateComponent(sbd)) {
								if (debugEnabled) {
									logger.debug("Identified candidate component class: " + resource);
								}
								candidates.add(sbd);
							}
							else {
								if (debugEnabled) {
									logger.debug("Ignored because not a concrete top-level class: " + resource);
								}
							}
						}
						else {
							if (traceEnabled) {
								logger.trace("Ignored because not matching any filter: " + resource);
							}
						}
					}
					catch (Throwable ex) {
						throw new BeanDefinitionStoreException(
								"Failed to read candidate component class: " + resource, ex);
					}
				}
				else {
					if (traceEnabled) {
						logger.trace("Ignored because not readable: " + resource);
					}
				}
			}
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
		}
		return candidates;
	}

下面就断点进去看一下那个方法,就是之前把过滤条件传进来了,此处就可以获得了。

拿到所有的Feign客户端就准备开始注册了

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

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

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