今天起了大早,偶發現,項目忘記加入註解@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;
}