spring 掃描BeanDefinition詳解以及TypeFilter擴充套件點
前言
本篇主要講解 在指定的基礎包中執行掃描註冊BeanDefinition: doscan(String... basePackages) 方法以及掃描過濾,匹配。
解析
主要方法:ClassPathBeanDefinitionScanner#doScan(String... basePackages)在指定的基礎包中執行掃描,返回已註冊的bean定義。
protected Set<BeanDefinitionHolder> doScan(String... basePackages) { Assert.notEmpty(basePackages, "At least one base package must be specified"); Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>(); for (String basePackage : basePackages) { //掃描候選元件的類路徑 Set<BeanDefinition> candidates = findCandidateComponents(basePackage); for (BeanDefinition candidate : candidates) { ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); candidate.setScope(scopeMetadata.getScopeName()); //生成bean名稱 String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); if (candidate instanceof AbstractBeanDefinition) { postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); } if (candidate instanceof AnnotatedBeanDefinition) { AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); } if (checkCandidate(beanName, candidate)) { BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); beanDefinitions.add(definitionHolder); //註冊bean定義 registerBeanDefinition(definitionHolder, this.registry); } } } return beanDefinitions; }複製程式碼
1、ClassPathScanningCandidateComponentProvider#findCandidateComponents(basePackage), 掃描候選元件的類路徑
public Set<BeanDefinition> findCandidateComponents(String basePackage) { if (this.componentsIndex != null && indexSupportsIncludeFilters()) { //根據索引獲得bean定義,配合spring-context-indexer使用,有興趣得自己去了解一下,這裡不做具體得解析 return addCandidateComponentsFromIndex(this.componentsIndex, basePackage); } else { //掃描獲得bean定義 return scanCandidateComponents(basePackage); } }複製程式碼
2、ClassPathScanningCandidateComponentProvider#scanCandidateComponents(String basePackage)
private Set<BeanDefinition> scanCandidateComponents(String basePackage) { Set<BeanDefinition> candidates = new LinkedHashSet<>(); try { //classpath*:com/zxj/***/service/**/*.classant path模式串 String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + '/' + this.resourcePattern; //用ant path獲取到檔案內容,檔案和path(class檔案) Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath); boolean traceEnabled = logger.isTraceEnabled(); boolean debugEnabled = logger.isDebugEnabled(); for (Resource resource : resources) { if (traceEnabled) { logger.trace("Scanning " + resource); } if (resource.isReadable()) { try { //讀取源資訊 MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource); //過濾匹配排除excludeFilters排除過濾器(可以沒有),包含includeFilter中的包含過濾器(至少包含一個)。 if (isCandidateComponent(metadataReader)) { ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); sbd.setResource(resource); sbd.setSource(resource); //判斷是否是合格的bean定義 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); } } } else { if (traceEnabled) { logger.trace("Ignored because not matching any filter: " + resource); } } } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to read candidate component class: " + resource, ex); } } else { if (traceEnabled) { logger.trace("Ignored because not readable: " + resource); } } } } catch (IOException ex) { throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex); } return candidates; } 複製程式碼
3、ClassPathScanningCandidateComponentProvider#isCandidateComponent(MetadataReader metadataReader)
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; } 複製程式碼
excludeFilters和includeFilters有那幾種定義呢?
過濾器型別 |
示例表達 |
描述 |
---|---|---|
annotation (default) |
|
要在目標元件中的型別級別出現的註釋。 |
assignable |
|
目標元件可分配給(擴充套件或實現)的類(或介面)。 |
aspectj |
|
要由目標元件匹配的AspectJ型別表示式。 |
regex |
|
要由目標元件類名匹配的正則表示式。 |
custom |
|
org.springframework.core.type .TypeFilter
介面的自定義實現 。 |
例如:
@Configuration @ComponentScan(basePackages = "org.example", includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"), excludeFilters = @Filter(Repository.class)) public class AppConfig { ... }複製程式碼
預設註冊的ClassPathScanningCandidateComponentProvider#registerDefaultFilters包含過濾器AnnotationTypeFilter(Component.class)。 作用:@Component及其子類都會匹配到。
@Component子類:@ Repository @ Controller @service
protected void registerDefaultFilters() { this.includeFilters.add(new AnnotationTypeFilter(Component.class)); ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader(); try { this.includeFilters.add(new AnnotationTypeFilter( ((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false)); logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning"); } catch (ClassNotFoundException ex) { // JSR-250 1.1 API (as included in Java EE 6) not available - simply skip. } try { this.includeFilters.add(new AnnotationTypeFilter( ((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false)); logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning"); } catch (ClassNotFoundException ex) { // JSR-330 API not available - simply skip. } }複製程式碼
這裡就體現了一個擴充套件點,例如我自己定義一個@myservice註解,在匹配的時候就會匹配到,就能夠被掃描進bean定義容器。
@Target({ ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface MyService { String value() default ""; } 複製程式碼
4、具體的匹配方法:
1、AbstractTypeHierarchyTraversingFilter#match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
// @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { // This method optimizes avoiding unnecessary creation of ClassReaders // as well as visiting over those readers. //匹配自己 if (matchSelf(metadataReader)) { return true; } ClassMetadata metadata = metadataReader.getClassMetadata(); if (matchClassName(metadata.getClassName())) { return true; } if (this.considerInherited) { String superClassName = metadata.getSuperClassName(); if (superClassName != null) { // Optimization to avoid creating ClassReader for super class. Boolean superClassMatch = matchSuperClass(superClassName); if (superClassMatch != null) { if (superClassMatch.booleanValue()) { return true; } } else { // Need to read super class to determine a match... try { if (match(metadata.getSuperClassName(), metadataReaderFactory)) { return true; } } catch (IOException ex) { logger.debug("Could not read super class [" + metadata.getSuperClassName() + "] of type-filtered class [" + metadata.getClassName() + "]"); } } } }} 複製程式碼
2、AnnotationTypeFilter#matchSelf(MetadataReader metadataReader)
@Override //父類定義的鉤子方法,子類實現。 匹配註解 protected boolean matchSelf(MetadataReader metadataReader) { AnnotationMetadata metadata = metadataReader.getAnnotationMetadata(); //metadata.hasAnnotation(this.annotationType.getName()) 例如如果當前類上的註解為@Service,那麼元註解的annotationSet屬性則為Service 自己 return metadata.hasAnnotation(this.annotationType.getName()) || //metadata.hasMetaAnnotation(this.annotationType.getName())例如如果當前類上的註解為@Service,那麼元註解的metaAnotationMap則有@Service和@Component,自己和父類 (this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName())); }複製程式碼
裡面的擴充套件還有:org.springframework.core.type .TypeFilter 介面的自定義實現,去匹配註解,或者其他,匹配完成後註冊bean定義。例如:
public class MyTypeFilter implements TypeFilter { @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { // 使用metadataReader中的類資訊、註解資訊來進行你的過濾判斷邏輯 return metadataReader.getClassMetadata().getClassName().equals(Abean.class.getName()); } } 複製程式碼