Feign源码学习(2)— Feign的启动流程 (扫描feign包下的类信息)

870 阅读3分钟

刚到新的公司有点忙,这周终于有时间结合SS老师的课程来学习源码。来吧,继续学起来。

一、feign的启动流程

上一期讲的是Feign的猜测的启动流程,现在,直接从源码入手。

上一系列的图,希望不会晕车。其实,我觉得看源码其实挺难的,特别是看别人的博客文章,简直一头雾水,菜鸡的我一样是有这种问题。所以我会边学习边输出,这样也是自己最好的一个学习方法。

从FeignClients注解就有一个关键的 @import 标签。

FeignClients注解

进入 FeignClientsRegistrar 这个类中。

我们可以看到 FeignClientsRegistrar 这个类中,进行了ImportBeanDefinitionRegistrar 的接口实现

OK, 紧接着就是调用了下面的 FeignClientsRegistrar#registerBeanDefinitions 方法,进行一个重写。

PS: 因为Spring上下文启动的时候,会调用这个方法进行相关的初始化

OK, 回到正文,就从FeignClientsRegistrar#registerBeanDefinitions 方法开始进行分析

二、registerBeanDefinitions分析

2.1 registerDefaultConfiguration(metadata, registry);

直接上代码:

private void registerDefaultConfiguration(AnnotationMetadata metadata,
			BeanDefinitionRegistry registry) {
            // 获取@EnableFeignClients上的attrs属性值,封装到对应的一个Map中
		Map<String, Object> 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"));
		}
	}

其实这里没啥特别重要的:

  1. 把@EnableFeignClients注解上的属性,进行一个属性的注入,注入到Map容器上。 这儿需要关注一个重点对象: metadata .getAnnotationAttributes(EnableFeignClients.class.getName(), true); 批注①

2.2 registerFeignClients(metadata, registry)分析

这里面代码太长,一下子拉所有的代码出来比较难懂,所以这里使用局部分析的方式。

2.2.1 扫描属性
  public void registerFeignClients(..){

	ClassPathScanningCandidateComponentProvider scanner = getScanner();
   	scanner.setResourceLoader(this.resourceLoader);

   	Set<String> basePackages;

   	Map<String, Object> attrs = metadata
   			.getAnnotationAttributes(EnableFeignClients.class.getName());
   	AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
   			FeignClient.class);
   	final Class<?>[] clients = attrs == null ? null
   			: (Class<?>[]) attrs.get("clients");
               }
               
             // 省略以下的代码
             ....
             registerFeignClient(...)

重点关注这行代码:

Map<String, Object> attrs = metadata
  			.getAnnotationAttributes(EnableFeignClients.class.getName());

刚刚我们提到 批注①做的事情,就是将@EnableFeignClients注解上的属性丢到Map上去进行保存。

关注到以下代码:

 Class<?>[] clients = attrs == null ? null
   			: (Class<?>[]) attrs.get("clients");

那么就是获得加了@EnableFeignClients注解的类名。
PS: 后续就是spring的扫包流程,并且for循环将对应的bean装配到容器中。

对了,扫包完之后,registerFeignClients 后面,就是一个注册feignClient的流程。

2.2.2 registerFeignClients方法分析

这里的代码比较简单,都是做一些属性的装配。。

private void registerFeignClient(BeanDefinitionRegistry registry,
   		AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
   	String className = annotationMetadata.getClassName();
       
       // 这行FeignClientFactoryBean需要去特殊注意
   	BeanDefinitionBuilder definition = BeanDefinitionBuilder
   			.genericBeanDefinition(FeignClientFactoryBean.class);
   	validate(attributes);
       
       // 属性的注入
   	definition.addPropertyValue("url", getUrl(attributes));
   	definition.addPropertyValue("path", getPath(attributes));
   	String name = getName(attributes);
   	definition.addPropertyValue("name", name);
   	String contextId = getContextId(attributes);
   	definition.addPropertyValue("contextId", contextId);
   	definition.addPropertyValue("type", className);
   	definition.addPropertyValue("decode404", attributes.get("decode404"));
   	definition.addPropertyValue("fallback", attributes.get("fallback"));
   	definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
   	definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);

   	String alias = contextId + "FeignClient";
   	AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();

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

   	beanDefinition.setPrimary(primary);

   	String qualifier = getQualifier(attributes);
   	if (StringUtils.hasText(qualifier)) {
   		alias = qualifier;
   	}

   	// 注册到spring容器中去
   	BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
   			new String[] { alias });
   	BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
   }

在这个方法,我们需要重点关注的点是:

BeanDefinitionBuilder definition = BeanDefinitionBuilder
   			.genericBeanDefinition(FeignClientFactoryBean.class);

其实也就是 FeignClientFactoryBean 这个类。PS: 这个类实际上就是feign动态代理生成的核心类。

到这里,feign的包扫描就已经分析完了。
1.通过@EnableFeign注解,对feign的类进行一个标识,并且将其对应的属性进行容器的注入,交给spring来进行管理。
2. 重点关注,FeignClientFactoryBean这个类,这个类是Feign动态代理的关键类,Feign的动态代理就是通过它来进行完成。