文章目录
- 1.FeignClientsRegistrar
- 2.完成配置注册
- 2.1 registerDefaultConfiguration方法
- 2.2 迭代稳定性
- 2.3 registerFeignClients方法
1.FeignClientsRegistrar
FeignClientsRegistrar实现ImportBeanDefinitionRegistrar接口。
2.完成配置注册
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {// 注册默认的Configuration(其实就是在解析@EnableFeignClients注解)this.registerDefaultConfiguration(metadata, registry);// 1)扫描所有的@FeignClient接口, 即扫描到Feign接口// 2)将每个@FeignClient注解的configuration属性注册进一个缓存map// 3)根据@FeignClient注解元数据生成的FeignClientBeanFactory的BeanDefinition, // 并将这个BeanDefinition注册进一个mapthis.registerFeignClients(metadata, registry);}
- registerDefaultConfiguration方法
- registerFeignClients方法
导入的类元数据就是启动类, 通过这个类的元数据可以获取到它上面所有的注解信息。
2.1 registerDefaultConfiguration方法
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"));}
}
registerClientConfiguration():
//FeignClientsRegistrar.java
private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,Object configuration) {//获取一个BeanDefinition的构建者 专门构建FeignClientSpecification的BeanDefinitionBeanDefinitionBuilder 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.registerBeanDefinition():
//DefaultListableBeanFactory.java
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)throws BeanDefinitionStoreException {//此时beanName为 default.启动类类名.FeignClientSpecificationAssert.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_INFRASTRUCTUREif (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 + "]");}}//覆盖,放入beanDefinitionMapthis.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);}
}
2.2 迭代稳定性
多线程的情况, 需要复制一个新的集合, 在新的集合中添加元素后, 替换原来旧集合。
-
保证多线程情况下共享变量的可见性, 添加了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的元素就返回trueupdateManualSingletonNames(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);}}
}
updateManualSingletonNames方法:
此方法不仅有迭代稳定性, 而且有双锁DCL
2.3 registerFeignClients方法
//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() {@Overrideprotected 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) {//扫描包,获取候选组件的BeanDefinitionSet<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);for (BeanDefinition candidateComponent : candidateComponents) {//判断是否是具有注解元数据的BeanDefinitionif (candidateComponent instanceof AnnotatedBeanDefinition) {// verify annotated class is an interfaceAnnotatedBeanDefinition 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的BeanDefinitionregisterFeignClient(registry, annotationMetadata, attributes);}}}
}
- getBasePackages: clients属性为空, 扫描其路径。
- getCanonicalName: 获取规范类名。
- getClientName: 获取FeignClient的服务名称。
- registerFeignClient: 注册FeignClient的FactoryBean。
- getBasePackages
//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;
}
这些属性指定的扫描包路径, 是一个并集的关系。
- 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 + "[]";elsereturn 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();}
}
- getClientName
//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 > serviceIdthrow new IllegalStateException("Either 'name' or 'value' must be provided in @"+ FeignClient.class.getSimpleName());
}
- registerFeignClient
//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的BeanDefinitionBeanDefinitionBuilder 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// nullbeanDefinition.setPrimary(primary);String qualifier = getQualifier(attributes);if (StringUtils.hasText(qualifier)) {alias = qualifier;}//包装成BeanDefinitionHolderBeanDefinitionHolder 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.// 获取beanNameString 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);}}
}