今天起了大早,偶发现,项目忘记加入注解@EnableFeignClients,导致启动失败,于是想知道它做了啥呢?为什么项目没有加载相关bean?

原来@EnableFeignClients是通过@Import把FeignClientsRegistrar注入到IOC容器中,当项目启动执行

invokeBeanFactoryPostProcessors--》

invokeBeanDefinitionRegistryPostProcessors—》

ConfigurationClassPostProcessor调用processConfigBeanDefinitions方法执行parser.parse(candidates)的时候会调用扫描解析类ClassPathBeanDefinitionScanner默认扫描并默认加载启动项包下的所有class(这里出现了个疑问,是否系统只注册@Component注解的类,这个问题一会看下代码),之后在执行reader.loadBeanDefinitions的时候会调用其他注册器进行注册--》

ConfigurationClassBeanDefinitionReader有两个方法,loadBeanDefinitionsFromImportedResources和loadBeanDefinitionsFromRegistrars,而本次使用的是loadBeanDefinitionsFromRegistrars,具体执行的注册器是FeignClientsRegistrar

  注册过程FeignClientsRegistrar

public void registerFeignClients(AnnotationMetadata metadata,
			BeanDefinitionRegistry registry) {
		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);
		// 省略。。。。
		for (String basePackage : basePackages) {
			Set<BeanDefinition> candidateComponents = scanner
					.findCandidateComponents(basePackage);
			for (BeanDefinition candidateComponent : candidateComponents) {
				if (candidateComponent instanceof AnnotatedBeanDefinition) {
					 // 省略。。。。
                    // 这里找到客户端FeignClient注解,准备注册到            DefaultListableBeanFactory容器里
					Map<String, Object> attributes = annotationMetadata
							.getAnnotationAttributes(
									FeignClient.class.getCanonicalName());

					String name = getClientName(attributes);
					registerClientConfiguration(registry, name,
							attributes.get("configuration"));

					registerFeignClient(registry, annotationMetadata, attributes);
				}
			}
		}
	}
private void registerFeignClient(BeanDefinitionRegistry registry,
			AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
		String className = annotationMetadata.getClassName();
		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;
		}

		BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
				new String[] { alias });
        // 注册Feign客户端到DefaultListableBeanFactory容器,这样启动后就能通过@Autowired注入,否则无法启动
		BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
	}

ClassPathBeanDefinitionScanner分析是否系统只注册@Component注解的类

省略其中调用过程,直接进入ClassPathScanningCandidateComponentProvider分析doScan方法,紧接着进入scanCandidateComponents,顾名思义应该就是只注册@Component注解的类,

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
		Set<BeanDefinition> candidates = new LinkedHashSet<>(); 
            // 省略。。。。
			for (Resource resource : resources) {
				if (traceEnabled) {
					logger.trace("Scanning " + resource);
				}
				if (resource.isReadable()) {
					try {
						MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
                        // 过滤条件
						if (isCandidateComponent(metadataReader)) {
							ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
							sbd.setResource(resource);
							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);
								}
							}
						}
						// 省略。。。。
			 
		return candidates;
	}

过滤条件中includeFileter包括Component:

确定给定的类是否与任何排除筛选器不匹配
与至少一个包含筛选器匹配。

protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
		for (TypeFilter tf : this.excludeFilters) {
			if (tf.match(metadataReader, getMetadataReaderFactory())) {
				return false;
			}
		}
		for (TypeFilter tf : this.includeFilters) {
			if (tf.match(metadataReader, getMetadataReaderFactory())) {
				return isConditionMatch(metadataReader);
			}
		}
		return false;
	}

 

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐