Spring Cloud OpenFeign 源码解析(二)Feign Client 的创建

标签: # Spring Cloud  Spring Cloud  spring cloud  OpenFeign  源码解析

Feign Client 的创建

1. 入口 FeignClientsRegistrar

入口:
在这里插入图片描述
在这里插入图片描述

FeignClientsRegistrar其实现了一个很重要的接口ImportBeanDefinitionRegistrar:
在这里插入图片描述

这个接口专门配合@Import注解、Configuration类使用的
在这里插入图片描述

可以获取到@Import注解所在的配置类的类元数据信息,然后根据导入的类元数据,按需注册Bean Definition:
在这里插入图片描述

2. 完成配置注册

我们看FeignClientsRegistrar对registerBeanDefinitions方法的实现:

//FeignClientsRegistrar.java
public void registerBeanDefinitions(AnnotationMetadata metadata,
		BeanDefinitionRegistry registry) {
	//注册默认的Configuration(其实就是在解析@EnableFeignClients注解)
	registerDefaultConfiguration(metadata, registry);
	//注册FeignClients(扫描所有@FeignClient注解并解析)
	registerFeignClients(metadata, registry);
}

DEBUG,看下一导入的类元数据,就是启动类,通过这个类的元数据可以获取到它上面所有的注解信息:
在这里插入图片描述

2.1 解析@EnableFeignClients注解

看registerDefaultConfiguration:

//FeignClientsRegistrar.java
private void registerDefaultConfiguration(AnnotationMetadata metadata,
		BeanDefinitionRegistry registry) {
	//从类元数据中获取@EnableFeignClients注解
	//getAnnotationAttributes:获取类上指定注解的属性
	//该方法第二个参数true,表示将注解中class类型的属性转换为字符串类名暴露到返回到map中
	Map<String, Object> defaultAttrs = metadata
			.getAnnotationAttributes(EnableFeignClients.class.getName(), true);
			
	//处理defaultConfiguration属性
	if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
		String name;
		//返回当前类是否在封闭类中声明(例如,当前类是一个内部/嵌套类,还是一个方法中的本地类)。
		//false代表当前类就是顶级类,此时是启动类,肯定是顶级类
		if (metadata.hasEnclosingClass()) {
			//如果当前是内部、嵌套、方法中的类,获取我的封闭类的类名
			name = "default." + metadata.getEnclosingClassName();
		}
		else {
			//返回false代表当前是就顶级类,直接获取类名
			name = "default." + metadata.getClassName();
		}
		registerClientConfiguration(registry, name,
				defaultAttrs.get("defaultConfiguration"));
	}
}

DEBUG看一下name值,可以看到刚好是default + 当前启动类的全限定性类名
在这里插入图片描述

继续看registerClientConfiguration:

//FeignClientsRegistrar.java
private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
		Object configuration) {
	//获取一个BeanDefinition的构建者 专门构建FeignClientSpecification的BeanDefinition
	BeanDefinitionBuilder builder = BeanDefinitionBuilder
			.genericBeanDefinition(FeignClientSpecification.class);
	//为FeignClientSpecification的构造器设置参数
	builder.addConstructorArgValue(name);
	builder.addConstructorArgValue(configuration);
	//builder.getBeanDefinition()会构建对应的BeanDefinition实例
	//然后将其注册到Spring的注册表中
	registry.registerBeanDefinition(
			name + "." + FeignClientSpecification.class.getSimpleName(),
			builder.getBeanDefinition());
}

//BeanDefinitionBuilder.java
/**
 * Add an indexed constructor arg value. The current index is tracked internally
 * and all additions are at the present point.
 * 添加一个索引构造函数arg值。 内部跟踪当前索引,所有添加都在当前位置。
 */
public BeanDefinitionBuilder addConstructorArgValue(@Nullable Object value) {
	this.beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(
			this.constructorArgIndex++, value);
	return this;
}

将BeanDefinition注册到Spring注册表:

//DefaultListableBeanFactory.java
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
		throws BeanDefinitionStoreException {
	//此时beanName为  default.启动类类名.FeignClientSpecification
	Assert.hasText(beanName, "Bean name must not be empty");
	Assert.notNull(beanDefinition, "BeanDefinition must not be null");

	if (beanDefinition instanceof AbstractBeanDefinition) {
		try {
			//验证这个bean定义。
			((AbstractBeanDefinition) beanDefinition).validate();
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
					"Validation of bean definition failed", ex);
		}
	}
	//先尝试从注册表中获取
	BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
	if (existingDefinition != null) {
		//如果已经存在,判断是否允许覆盖,不允许就抛异常
		if (!isAllowBeanDefinitionOverriding()) {
			throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
		}
		else if (existingDefinition.getRole() < beanDefinition.getRole()) {
			// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
			if (logger.isInfoEnabled()) {
				logger.info("Overriding user-defined bean definition for bean '" + beanName +
						"' with a framework-generated bean definition: replacing [" +
						existingDefinition + "] with [" + beanDefinition + "]");
			}
		}
		else if (!beanDefinition.equals(existingDefinition)) {
			if (logger.isDebugEnabled()) {
				logger.debug("Overriding bean definition for bean '" + beanName +
						"' with a different definition: replacing [" + existingDefinition +
						"] with [" + beanDefinition + "]");
			}
		}
		else {
			if (logger.isTraceEnabled()) {
				logger.trace("Overriding bean definition for bean '" + beanName +
						"' with an equivalent definition: replacing [" + existingDefinition +
						"] with [" + beanDefinition + "]");
			}
		}
		//覆盖,放入beanDefinitionMap
		this.beanDefinitionMap.put(beanName, beanDefinition);
	}
	else {
		//第一次注册
		//检查该工厂bean创建阶段是否已经开始,通过在此期间是否有任何bean被标记为已创建来判断
		if (hasBeanCreationStarted()) {
			// Cannot modify startup-time collection elements anymore (for stable iteration)
			// 不能修改启动中的集合元素(用于稳定的迭代)
			synchronized (this.beanDefinitionMap) {
				//先放入注册表
				this.beanDefinitionMap.put(beanName, beanDefinition);
				//beanDefinitionNames是一个可供遍历的beanName集合,bean创建阶段就是
				//先遍历该集合通过beanName再从beanDefinitionMap中获取BeanDefinition的
				
				//所以在创建阶段为了保证集合迭代稳定性,需要创建新的集合在新的集合上进行修改
				//创建一个新list,将beanDefinitionNames内容添加进去
				List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
				updatedDefinitions.addAll(this.beanDefinitionNames);
				//把新的beanName放到新的list中
				updatedDefinitions.add(beanName);
				//将新的集合替代旧的集合
				this.beanDefinitionNames = updatedDefinitions;
				//从工厂内部的手动单例名称集中删除指定名称,避免重复注册
				removeManualSingletonName(beanName);
			}
		}
		else {
			// Still in startup registration phase
			//仍处于启动注册阶段,不用考虑集合迭代稳定性问题
			//直接放入注册表、添加到可遍历集合中
			this.beanDefinitionMap.put(beanName, beanDefinition);
			this.beanDefinitionNames.add(beanName);
			//从工厂内部的手动单例名称集中删除指定名称,避免重复注册
			removeManualSingletonName(beanName);
		}
		this.frozenBeanDefinitionNames = null;
	}

	if (existingDefinition != null || containsSingleton(beanName)) {
		resetBeanDefinition(beanName);
	}
}

集合迭代稳定性:
在这里插入图片描述
为什么多线程的情况,需要复制一个新的集合,在新的集合中添加元素后,替换原来旧集合?为了迭代稳定性。

  • 首先为了保证多线程情况下共享变量的可见性(主内存和工作内存之间的可见性),添加了volatile修饰:
    在这里插入图片描述
  • 其次为了保证有序性、原子性(多线程并发修改造成问题),所以添加了synchronized关键字
    在这里插入图片描述
  • 迭代稳定性:
    一个线程正在修改集合中的数据,另一个线程正在迭代读取集合中的数据,由于加了volatile,导致读线程迭代的过程中,写线程对集合中的修改读线程是立即可见的,读线程读取的数据正好是写线程修改的数据,或者读线程一开始获取的个数是10个,遍历过程中,数量变多了变少了,发生这些变化都代表不稳定,并有可能引发错误。


    解决方案:修改的线程在原来集合基础上复制一个新的集合进行修改,等所有修改完成后,将整个新的集合替换掉原来旧的集合,而在修改过程中,其他线程访问的集合的地址还是指向旧的(类似写时复制的感觉)
    如果使用JUC并发包的集合,严重影响性能。

看下从工厂内部的手动单例名称集中删除指定名称,避免重复注册:

//DefaultListableBeanFactory.java
private void removeManualSingletonName(String beanName) {
	//Consumer:对给定的参数执行此操作。
	//set -> set.remove(beanName):对给定的set集合删除key为beanName的元素
	
	//Predicate:对给定参数计算此谓词。
	//set -> set.contains(beanName):对给定的set集合进行判断,包含key为beanName的元素就返回true
	updateManualSingletonNames(set -> set.remove(beanName), set -> set.contains(beanName));
}

//DefaultListableBeanFactory.java
//更新工厂内部的手动单例名称集。
private void updateManualSingletonNames(Consumer<Set<String>> action, Predicate<Set<String>> condition) {
	//检查这个工厂的bean创建阶段是否已经开始
	if (hasBeanCreationStarted()) {//一样存在迭代稳定性问题
		// Cannot modify startup-time collection elements anymore (for stable iteration)
		// 不能修改启动中的集合元素(用于稳定的迭代)
		synchronized (this.beanDefinitionMap) {
			if (condition.test(this.manualSingletonNames)) {
				//复制了一个集合,对复制的集合进行操作
				Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
				action.accept(updatedSingletons);
				this.manualSingletonNames = updatedSingletons;
			}
		}
	}
	else {
		// Still in startup registration phase
		// 判断this.manualSingletonNames这个集合是否包含key为beanName的元素
		if (condition.test(this.manualSingletonNames)) {
			//包含了就从manualSingletonNames集合删除这个key为beanName的元素
			action.accept(this.manualSingletonNames);
		}
	}
}

Spring注册表中专门有一个集合,在使用org.springframework.beans.factory.support.DefaultListableBeanFactory#registerSingleton方法手动注册单例BeanDefinition时,会将其放到manualSingletonNames这个集合,beanDefinitionNames和manualSingletonNames中的元素不能重复,所以这里做了处理。

2.2 扫描所有@FeignClient注解并解析

回到FeignClientsRegistrar的registerBeanDefinitions,现在看registerFeignClients方法:

//FeignClientsRegistrar.java
public void registerBeanDefinitions(AnnotationMetadata metadata,
		BeanDefinitionRegistry registry) {
	//注册默认的Configuration(其实就是在解析@EnableFeignClients注解)
	registerDefaultConfiguration(metadata, registry);
	//注册FeignClients(扫描所有@FeignClient注解并解析)
	registerFeignClients(metadata, registry);
}
//FeignClientsRegistrar.java
public void registerFeignClients(AnnotationMetadata metadata,
		BeanDefinitionRegistry registry) {
	//获取扫描器
	ClassPathScanningCandidateComponentProvider scanner = getScanner();
	//设置资源加载器
	scanner.setResourceLoader(this.resourceLoader);

	Set<String> basePackages;
	//获取@EnableFeignClients注解的属性
	Map<String, Object> attrs = metadata
			.getAnnotationAttributes(EnableFeignClients.class.getName());
	//定义扫描过滤器,专门指定扫描被@FeignClient注解的类
	AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
			FeignClient.class);
	//获取@EnableFeignClients注解的clients属性
	//该属性直接指定要加载哪些@FeignClient类,配置了这个属性就只会加载指定的
	final Class<?>[] clients = attrs == null ? null
			: (Class<?>[]) attrs.get("clients");
	if (clients == null || clients.length == 0) {
		//clients属性为空,则为扫描器指定条件,只扫描被@FeignClient注解的类
		scanner.addIncludeFilter(annotationTypeFilter);
		//获取扫描路径
		basePackages = getBasePackages(metadata);
	}
	else {
		//clients不空的情况
		final Set<String> clientClasses = new HashSet<>();
		basePackages = new HashSet<>();
		for (Class<?> clazz : clients) {//直接遍历指定的@FeignClient类
			//获取类所在的包路径
			basePackages.add(ClassUtils.getPackageName(clazz));
			//获取类的规范类名
			clientClasses.add(clazz.getCanonicalName());
		}
		//定义扫描过滤器,只扫描clientClasses中包含的类
		AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {
			@Override
			protected boolean match(ClassMetadata metadata) {
			    //对扫描到的类进行匹配:
			    //获取当前扫描到的类的类名,转换成规范类名(处理内部类的情况)
				String cleaned = metadata.getClassName().replaceAll("\\$", ".");
				//判断clientClasses中是否包含这个类
				return clientClasses.contains(cleaned);
			}
		};
		//即要同时满足被@FeignClient注解,同时该类在@FeignClientd的clients属性中被指定
		scanner.addIncludeFilter(
				new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));
	}

	for (String basePackage : basePackages) {
		//扫描包,获取候选组件的BeanDefinition
		Set<BeanDefinition> candidateComponents = scanner
				.findCandidateComponents(basePackage);
		for (BeanDefinition candidateComponent : candidateComponents) {
			//判断是否是具有注解元数据的BeanDefinition
			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");
				//获取@FeignClient注解的属性
				Map<String, Object> attributes = annotationMetadata
						.getAnnotationAttributes(
								FeignClient.class.getCanonicalName());
				//获取FeignClient的名称(即@FeignClient注解的四个属性)
				//优先级contextId > value > name > serviceId
				//就是服务id、服务名称
				String name = getClientName(attributes);
				//注册ClientConfiguration,之前跟过
				//就是注册FeignClientSpecification,FeignClient规范
				registerClientConfiguration(registry, name,
						attributes.get("configuration"));
				//注册FeignClient的FactoryBean的BeanDefinition
				registerFeignClient(registry, annotationMetadata, attributes);
			}
		}
	}
}

大致流程看了,现在分别看一下几个分支:

2.2.1 clients属性为空,获取扫描路径:

//FeignClientsRegistrar.java
protected Set<String> getBasePackages(AnnotationMetadata importingClassMetadata) {
	//获取@EnableFeignClients注解的属性
	Map<String, Object> attributes = importingClassMetadata
			.getAnnotationAttributes(EnableFeignClients.class.getCanonicalName());

	Set<String> basePackages = new HashSet<>();
	for (String pkg : (String[]) attributes.get("value")) {//获取value属性
		if (StringUtils.hasText(pkg)) {
			basePackages.add(pkg);
		}
	}
	for (String pkg : (String[]) attributes.get("basePackages")) {//获取basePackages属性
		if (StringUtils.hasText(pkg)) {
			basePackages.add(pkg);
		}
	}
	for (Class<?> clazz : (Class[]) attributes.get("basePackageClasses")) {//获取basePackageClasses属性
		//获取类所在的包路径
		basePackages.add(ClassUtils.getPackageName(clazz));
	}

	if (basePackages.isEmpty()) {
		//如果还为空,获取之前@Import注解所标记的那个类所在的包路径
		basePackages.add(
				ClassUtils.getPackageName(importingClassMetadata.getClassName()));
	}
	return basePackages;
}

可以看到这些属性指定的扫描包路径,是一个并集的关系。

2.2.2 clazz.getCanonicalName():获取规范类名

在这里插入图片描述

//Class.java
/**
 * Returns the canonical name of the underlying class as
 * defined by the Java Language Specification.  Returns null if
 * the underlying class does not have a canonical name (i.e., if
 * it is a local or anonymous class or an array whose component
 * type does not have a canonical name).
 * 返回Java语言规范定义的基础类的规范名称。如果基础类没有规范名称则返回null。
 * (例如,如果它是一个本地或匿名类,或者是一个元素类型没有规范名称的数组)
 * 
 * @since 1.5
 */
public String getCanonicalName() {
    if (isArray()) {
        //数组情况:
        //获取数组元素的规范名称
        String canonicalName = getComponentType().getCanonicalName();
        if (canonicalName != null)
            return canonicalName + "[]";
        else
            return null;
    }
    if (isLocalOrAnonymousClass())
        //本地或匿名类
        return null;
    Class<?> enclosingClass = getEnclosingClass();
    if (enclosingClass == null) { // top level class
        //当前类就是最顶层类
        return getName();
    } else {
        //内部类情况:
        String enclosingName = enclosingClass.getCanonicalName();
        if (enclosingName == null)
            return null;
        return enclosingName + "." + getSimpleName();
    }
}

2.2.3 获取Feign Client的服务名称

//FeignClientsRegistrar.java
private String getClientName(Map<String, Object> client) {
	//注意,client是@FeignClient注解的属性
	if (client == null) {
		return null;
	}
	String value = (String) client.get("contextId");
	if (!StringUtils.hasText(value)) {
		value = (String) client.get("value");
	}
	if (!StringUtils.hasText(value)) {
		value = (String) client.get("name");
	}
	if (!StringUtils.hasText(value)) {
		value = (String) client.get("serviceId");
	}
	if (StringUtils.hasText(value)) {
		return value;
	}
    //看到优先级contextId > value > name > serviceId
	throw new IllegalStateException("Either 'name' or 'value' must be provided in @"
			+ FeignClient.class.getSimpleName());
}

2.2.4 注册Feign Client的FactoryBean

//FeignClientsRegistrar.java
private void registerFeignClient(BeanDefinitionRegistry registry,
		AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
	//registry:Spring注册表
	//annotationMetadata:扫描到的被@FeignClient注解的类的注解数据
	//attributes:当前@FeignClient注解上相关的属性
	
	
	String className = annotationMetadata.getClassName();
	//FeignClientFactoryBean.class:FeignClient的工厂Bean,用来创建FeignClient实例的。
	
	//获取构建者构建FeignClientFactoryBean的BeanDefinition
	BeanDefinitionBuilder definition = BeanDefinitionBuilder
			.genericBeanDefinition(FeignClientFactoryBean.class);
	//@FeignClient注解的属性校验
	validate(attributes);
	//用@FeignClient注解的属性值为将来创建的FeignClientFactoryBean实例赋值
	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";
	//构建出beanDefinition实例
	AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
	
	//继续初始化,处理primary和qualifier属性配置
	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;
	}
	//包装成BeanDefinitionHolder
	BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
			new String[] { alias });
	//注册
	BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
//BeanDefinitionReaderUtils.java
public static void registerBeanDefinition(
		BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
		throws BeanDefinitionStoreException {

	// Register bean definition under primary name.
	// 获取beanName
	String beanName = definitionHolder.getBeanName();
	// 注册到注册表
	registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

	// Register aliases for bean name, if any.
	// 为beanName注册别名
	String[] aliases = definitionHolder.getAliases();
	if (aliases != null) {
		for (String alias : aliases) {
			registry.registerAlias(beanName, alias);
		}
	}
}

3. 完成自动配置

入口,从依赖开始:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
找到这个自动配置类:
在这里插入图片描述
可以看到这里将Spring容器中所有的FeignClient规范类实例都放入了FeignContext。

断点看一下:
在这里插入图片描述

4. 生成 Feign Client

现在看FeignClientFactoryBean,它是Spring的FactoryBean,我们看其getObject方法:

//FeignClientFactoryBean.java
public Object getObject() throws Exception {
	return getTarget();
}
//FeignClientFactoryBean.java
<T> T getTarget() {
	//根据spring容器,获取FeignContext,Feign的上下文,也是FeignClient的工厂类
	FeignContext context = this.applicationContext.getBean(FeignContext.class);
	//根据FeignContext,获取一个Feign的构建器
	Feign.Builder builder = feign(context);
	...
	
	return (T) targeter.target(this, builder, context,
			new HardCodedTarget<>(this.type, this.name, url));
}

4.1 从Feign Client子容器获取组件

看feign方法:

//FeignClientFactoryBean.java
protected Feign.Builder feign(FeignContext context) {
	//get方法:从FeignContext中获取对应类型的实例,底层会从当前FeignClient对应的子容器中获取
	
	//这里获取Feign的日志工厂
	FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
	Logger logger = loggerFactory.create(this.type);

	// @formatter:off
	//这里获取Feign的构建器
	//构建器的意义我们不需要关注复杂的构建流程,只需要给构建器传递一些需要的组件即可
	//这里主要往构建器放入一些FeignClient依赖的一些组件
	Feign.Builder builder = get(context, Feign.Builder.class)
			// required values
			.logger(logger)
			.encoder(get(context, Encoder.class))
			.decoder(get(context, Decoder.class))
			.contract(get(context, Contract.class));
	// @formatter:on
	//获取FeignClientProperties进行一些属性的配置
	configureFeign(context, builder);

	return builder;
}
//看其中一个get方法:
//FeignClientFactoryBean.java
protected <T> T get(FeignContext context, Class<T> type) {
	//注意,当前类是FeignClientFactoryBean
	//所以这个this.contextId实际上是当前FeignClient的服务id、微服务名称
	T instance = context.getInstance(this.contextId, type);
	if (instance == null) {
		throw new IllegalStateException(
				"No bean found of type " + type + " for " + this.contextId);
	}
	return instance;
}

继续看context.getInstance:

//NamedContextFactory.java,就是FeignContext.java
public <T> T getInstance(String name, Class<T> type) {
	//根据name先获取对应的子容器
	//name就是微服务名称,FeignClient的名称
	AnnotationConfigApplicationContext context = getContext(name);
	//根据类型从当前子容器,和子容器所有的祖先容器中查找bean的名称,判断是否存在
	if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context,
			type).length > 0) {
		//存在就返回对应类型的实例
		return context.getBean(type);
	}
	return null;
}

4.2 Feign Client子容器的创建

看获取子容器getContext方法:

//NamedContextFactory.java 
//FeignContext.java继承自NamedContextFactory.java
protected AnnotationConfigApplicationContext getContext(String name) {
	if (!this.contexts.containsKey(name)) {
		synchronized (this.contexts) {//双重检查锁,线程安全问题
			if (!this.contexts.containsKey(name)) {
				//子容器还不存在则进行创建
				this.contexts.put(name, createContext(name));
			}
		}
	}
	return this.contexts.get(name);
}
//创建子容器
//NamedContextFactory.java
protected AnnotationConfigApplicationContext createContext(String name) {
	AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
	//这里configurations存的就是各个feign client的规范类
	if (this.configurations.containsKey(name)) {
		//获取规范类中的配置类
		for (Class<?> configuration : this.configurations.get(name)
				.getConfiguration()) {
			//将对应服务名称的配置类注册到该容器
			context.register(configuration);
		}
	}
	for (Map.Entry<String, C> entry : this.configurations.entrySet()) {
		//default开头的是全局的规范类,存的是@EnableFeignClients的defaultConfiguration属性配置的配置类
		if (entry.getKey().startsWith("default.")) {
			for (Class<?> configuration : entry.getValue().getConfiguration()) {
				//将全局的配置类注册到该容器
				context.register(configuration);
			}
		}
	}
	//注册占位符配置解析器,可以解析bean定义属性值和{@code @Value}注解中的占位符。
	//注册默认配置类,defaultConfigType就是FeignClientsConfiguration.class
	context.register(PropertyPlaceholderAutoConfiguration.class,
			this.defaultConfigType);
	//添加具有最高优先级的给定属性源对象。
	context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(
			this.propertySourceName,
			Collections.<String, Object>singletonMap(this.propertyName, name)));
	if (this.parent != null) {
		// 关键!为当前容器设置父容器
		// Uses Environment from parent as well as beans 
		context.setParent(this.parent);
		// jdk11 issue
		// https://github.com/spring-cloud/spring-cloud-netflix/issues/3101
		context.setClassLoader(this.parent.getClassLoader());
	}
	context.setDisplayName(generateDisplayName(name));
	//刷新容器
	context.refresh();
	return context;
}

4.3 构建Feign Client实例

回到FeignClientFactoryBean,继续看getObject方法:

//FeignClientFactoryBean.java
public Object getObject() throws Exception {
	return getTarget();
}
//FeignClientFactoryBean.java
<T> T getTarget() {
	//根据spring容器,获取FeignContext,Feign的上下文,也是FeignClient的工厂类
	FeignContext context = this.applicationContext.getBean(FeignContext.class);
	//根据FeignContext,获取一个Feign的构建器
	//底层就是从当前feignClient名称对应的子容器中获取一些
	//  创建FeignClient所依赖的组件实例
	Feign.Builder builder = feign(context);
	//判断是否指定url属性,没有指定了就会负载均衡的方式进行远程调用
	if (!StringUtils.hasText(this.url)) {
		//为服务名补全协议
		if (!this.name.startsWith("http")) {
			this.url = "http://" + this.name;
		}
		else {
			this.url = this.name;
		}
		//拼接前缀,就是path属性,cleanPath会先格式化一下
		this.url += cleanPath();
		//没有指定url,使用具有负载均衡的远程调用客户端 构建feignClient
		return (T) loadBalance(builder, context,
				new HardCodedTarget<>(this.type, this.name, this.url));
	}
	//指定了url,则是直连方式
	if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
		//补全协议
		this.url = "http://" + this.url;
	}
	//拼接前缀,就是path属性,cleanPath会先格式化一下
	String url = this.url + cleanPath();
	//getOptional:也是从context中对应的feignClient名称的子容器中获取Client类型的实例
	//这个Client就是发起远程调用的客户端
	Client client = getOptional(context, Client.class);
	if (client != null) {
		//判断client是否是具有负载均衡的功能client,如果是的话取消包装
		//确保直连
		if (client instanceof LoadBalancerFeignClient) {
			// ribbon的负载均衡客户端
			// not load balancing because we have a url,
			// but ribbon is on the classpath, so unwrap
			// 没有负载平衡,因为我们有一个URL,但是ribbon在类路径中,所以请取消包装
			client = ((LoadBalancerFeignClient) client).getDelegate();
		}
		if (client instanceof FeignBlockingLoadBalancerClient) {
			// openFeign的负载均衡客户端
			// not load balancing because we have a url,
			// but Spring Cloud LoadBalancer is on the classpath, so unwrap
			// 因为我们有一个URL,所以没有负载均衡
			// 但是Spring Cloud LoadBalancer在类路径上,因此请取消包装
			client = ((FeignBlockingLoadBalancerClient) client).getDelegate();
		}
		builder.client(client);
	}
	//从子容器获取对应类型的实例
	Targeter targeter = get(context, Targeter.class);
	//直连方式创建
	return (T) targeter.target(this, builder, context,
			new HardCodedTarget<>(this.type, this.name, url));
}
  • cleanPath
    //FeignClientFactoryBean.java
    private String cleanPath() {
    	//对前缀,path属性进行格式化
    	String path = this.path.trim();
    	if (StringUtils.hasLength(path)) {
    		if (!path.startsWith("/")) {
    			path = "/" + path;
    		}
    		if (path.endsWith("/")) {
    			path = path.substring(0, path.length() - 1);
    		}
    	}
    	return path;
    }
    
  • getOptional
    //FeignClientFactoryBean.java
    protected <T> T getOptional(FeignContext context, Class<T> type) {
    	//这个this.contextId是当前FeignClient的服务id、微服务名称
    	//之前跟过了不看了,从对应子容器中获取指定类型实例
    	return context.getInstance(this.contextId, type);
    }
    

直连方式、负载均衡方式创建其实是一样的,继续跟:
在这里插入图片描述

//HystrixTargeter.java
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
		FeignContext context, Target.HardCodedTarget<T> target) {
	if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
		//没有开启熔断功能话就不是熔断的Builder走这
		return feign.target(target);
	}
	//如果开启了熔断,就会处理一些服务降级的配置:
	feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
	String name = StringUtils.isEmpty(factory.getContextId()) ? factory.getName()
			: factory.getContextId();
	SetterFactory setterFactory = getOptional(name, context, SetterFactory.class);
	if (setterFactory != null) {
		builder.setterFactory(setterFactory);
	}
	Class<?> fallback = factory.getFallback();
	if (fallback != void.class) {
		return targetWithFallback(name, context, target, builder, fallback);
	}
	Class<?> fallbackFactory = factory.getFallbackFactory();
	if (fallbackFactory != void.class) {
		return targetWithFallbackFactory(name, context, target, builder,
				fallbackFactory);
	}
	//也是调feign.target
	return feign.target(target);
}
//Feign.java
public <T> T target(Target<T> target) {
  return build().newInstance(target);
}

在这里插入图片描述

继续跟newInstance:

//ReflectiveFeign.java
public <T> T newInstance(Target<T> target) {
  //targetToHandlersByName.apply:生成方法处理器
  //返回值nameToHandler:
  //  key:当前feignClient的方法名
  //  value:方法处理器
  Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
  //methodToHandler:key是方法对象,value是方法处理器
  Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
  //默认方法处理器列表
  List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
  //遍历当前feignClient的接口的所有的方法
  for (Method method : target.type().getMethods()) {
    if (method.getDeclaringClass() == Object.class) {
      //Object的方法不处理
      continue;
    } else if (Util.isDefault(method)) {//是否是接口中的默认方法
      //默认方法创建一个默认方法处理器
      DefaultMethodHandler handler = new DefaultMethodHandler(method);
      //添加到默认方法处理器集合
      defaultMethodHandlers.add(handler);
      //保存方法和处理器映射关系
      methodToHandler.put(method, handler);
    } else {
      //不是默认方法,就是抽象方法
      //从nameToHandler获取已经生成好的对应的方法处理器
      methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
    }
  }
  //jdk动态代理,创建InvocationHandler,再创建代理对象
  InvocationHandler handler = factory.create(target, methodToHandler);
  T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
      new Class<?>[] {target.type()}, handler);

  for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
    defaultMethodHandler.bindTo(proxy);
  }
  return proxy;
}
  • 判断是否是接口中的默认方法Util.isDefault(method):

    //feign.Util.java
    public static boolean isDefault(Method method) {
      // Default methods are public non-abstract, non-synthetic, and non-static instance methods
      // declared in an interface.
      // method.isDefault() is not sufficient for our usage as it does not check
      // for synthetic methods. As a result, it picks up overridden methods as well as actual default
      // methods.
      // 默认方法是在接口中声明的公共非抽象,非合成和非静态实例方法。 
      // method.isDefault()不足以用于我们的用法,因为它不检查合成方法。 
      // 结果,它选择了覆盖的方法以及实际的默认方法。
    
      //SYNTHETIC也是修饰符,但是是编译器自动生成的,修饰方法或类
      //编译器通过生成一些在源代码中不存在的synthetic方法和类的方式,
      //实现了对private级别的字段和类的访问,从而绕开了语言限制,这可以算是一种trick。
      //一般用在内部类,例如外部类要访问内部类成员时,内部类成员都是私有的,
      //编译以后实际上会为内部类成员生成SYNTHETIC的get方法给外部类进行访问
      //参考:https://www.cnblogs.com/bethunebtj/p/7761596.html
      final int SYNTHETIC = 0x00001000;
      
      //获取当前方法修饰符,做与运算,如果只有public修饰,说明是实现方法(没有被abstract、static修饰)
      //又因为在接口中申明的,所以是接口的默认方法。
      return ((method.getModifiers()
          & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC | SYNTHETIC)) == Modifier.PUBLIC)
          && method.getDeclaringClass().isInterface();
    }
    

    在这里插入图片描述

    此时有public和abstract修饰,说明是抽象方法。

    在这里插入图片描述

    此时只有public修饰符,说明是一个实现方法,又在接口中,所以是默认方法。

  • 方法处理器生成
    看targetToHandlersByName.apply方法:

    //feign.ReflectiveFeign.ParseHandlersByName.java
    public Map<String, MethodHandler> apply(Target key) {
      //parseAndValidatateMetadata:调用它来解析类中链接到HTTP请求的方法。
      //方法元数据,这里只会获取接口中的抽象方法的元数据
      List<MethodMetadata> metadata = contract.parseAndValidatateMetadata(key.type());
      Map<String, MethodHandler> result = new LinkedHashMap<String, MethodHandler>();
      //遍历每一个方法元数据
      for (MethodMetadata md : metadata) {
        BuildTemplateByResolvingArgs buildTemplate;
        //buildTemplate主要构建http请求的
        if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
          buildTemplate = new BuildFormEncodedTemplateFromArgs(md, encoder, queryMapEncoder);
        } else if (md.bodyIndex() != null) {
          buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder, queryMapEncoder);
        } else {
          buildTemplate = new BuildTemplateByResolvingArgs(md, queryMapEncoder);
        }
        //初始化一个buildTemplate后,根据buildTemplate创建方法处理器
        //md.configKey():就是方法名,例如xxxService#saveDepart(Depart),没有包路径
        result.put(md.configKey(),
        	//factory = new InvocationHandlerFactory.Default()
            factory.create(key, md, buildTemplate, options, decoder, errorDecoder));
      }
      return result;
    }
    

    看一下创建方法处理器:

    //feign.SynchronousMethodHandler.Factory.java
    public MethodHandler create(Target<?> target,
                                MethodMetadata md,
                                RequestTemplate.Factory buildTemplateFromArgs,
                                Options options,
                                Decoder decoder,
                                ErrorDecoder errorDecoder) {
      //看到new 了一个SynchronousMethodHandler,同步方法处理器
      return new SynchronousMethodHandler(target, client, retryer, requestInterceptors, logger,
          logLevel, md, buildTemplateFromArgs, options, decoder,
          errorDecoder, decode404, closeAfterDecode, propagationPolicy);
    }
    
版权声明:本文为weixin_41947378原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_41947378/article/details/109183480

智能推荐

一致性hash算法

散列(hash)在我看来就是一个数组,而与数组不同的点在于数组是按顺序写入的,而hash是按照一定的hash算法确定元素在数组中的位置的。hash最难的问题在于会有冲突出现,如果两个object根据相应的hash算法得出的值一样便产生了hash冲突。在所有解决hash冲突的方法中,我最欣赏的是链式解决法,即将hash到同一位置的元素用链表连接。当然还有其它几种处理hash冲突的算法,比如建立公共溢...

OpenCV-Python learning-1.安装,图片读取显示

1. OpenCV与OpenGL区别 https://www.zhihu.com/question/20212016 一个是让机器识别东西的,OpenCV是给电脑做眼睛的。 一个是让机器计算出更好画面的,OpenGL用在游戏渲染方面很多。 OpenCV(Open Source Computer Vision Library)是一个基于(开源)发行的跨平台计算机视觉库,OpenGL(全写Open G...

Mycat+Mysql分布式架构改造和性能压力测试

架构实现 Mycat作为数据库高可用中间件具备很多的功能,如负载均衡,分库分表,读写分离,故障迁移等。结合项目的实际情况,分库分表功能对于关联查询有很高的要求,需要从业务角度考虑分库分表后的关联查询SQL的分析,业务代码动作较大,所以在此方案中我们不考虑分库分表。主要应用Mycat的负载均衡及故障迁移的功能即可。 整个架构改造包括两个部分,第一是单例Mysql改为多个Mysql,同时负载均衡,并且...

人脸识别之疲劳检测(二)阈值法、KNN分类和K-means聚类

Table of Contents 1、均值法 2、中值法 3、KNN 4、K-means 结合上一节在获得人眼特征点后需要对睁眼闭眼状态做出判断,方法的选择需要经验结合公平的评价方法,使用大量测试集得到不同方法下的精确度并做出比较: 1、均值法 50帧睁眼数据取均值,得到不同阈值下精确度。 2、中值法 50帧睁眼数据取中值,得到不同阈值下精确度。 3、KNN KNN是一种ML常用分类算法,通过测...

CodeForce Tic-Tac-Toe

Two bears are playing tic-tac-toe via mail. It's boring for them to play usual tic-tac-toe game, so they are a playing modified version of this game. Here are its rules. The game is played on the foll...

猜你喜欢

Python雾里看花-抽象类ABC (abstract base class)

首先认识模块 abc,python中没有提供抽象类与抽象方法,然而提供了内置模块abc来模拟实现抽象类,例如提供泛映射类型的抽象类 abc.MutableMapping 继承abc.MutableMapping构造一个泛映射类型(类似python中的dict) 当然继承abc.Mapping 也可以,毕竟MutableMapping是其子类 dict是python中典型的映射类型数据结构,其接口的...

python 文件操作

2, with open (‘xx.txt’,‘w’,encoding=‘utf-8’) as f: f.write(‘文件内容或对象’)...

【Python基础】使用统计函数绘制简单图形

机器学习算法与自然语言处理出品 @公众号原创专栏作者 冯夏冲 学校 | 哈工大SCIR实验室在读博士生 2.1 函数bar 用于绘制柱状图 2.2 函数barh 用于绘制条形图 2.3 函数hist 用于绘制直方图 直方图与柱状图的区别 函数pie 用于绘制饼图 2.5 函数polor 用于绘制极线图 极线图是在极坐标系上绘出的一种图。在极坐标系中,要确定一个点,需要指明这个点距原点的角...

css:顶部按钮固定,上面内容滑动

这种需求我们平时见到很多的,实现方法也多的参差不齐,下面我说一种简单的。如图: 可以看到只有红线部分滚动,底下按钮是固定的。 代码...

环形公路堵车概率模型(含详细解析)

文章目录 基础理论 代码实现 图形分析 基础理论 路面上有n辆车,以不同的速度向前行驶, 模拟堵车问题。 有以下假设: 假设某辆车的当前速度是v。 若前方可见范围内没车,则它在下一秒的车速提高到v+1,直到达到规定的最高限速。 若前方有车,前车的距离为d,且d < v,则它下 一秒的车速降低到d-1 。 每辆车会以概率p随机减速v-1。、 代码实现 图形分析 图形中颜色越重的地方,说明很多车...