在接上一篇之前, 我想先寫幾個測驗demo, 應該能幫助更好的理解.
demo:
com.study.elvinle.ioc.xml.IndexService:
public class IndexService { private String name = "index";public String getName() { System.out.println("IndexService.getName() --> " + name); return name; } public void setName(String name) { this.name = name; } }
com.study.elvinle.ioc.fac.IndexFactoryBean:
@Component("index")
public class IndexFactoryBean implements FactoryBean<Object> {
public void printf(){
System.out.println("IndexFactoryBean printf");
}
@Override
public Object getObject() throws Exception {
return new IndexService("小明");
}
@Override
public Class<?> getObjectType() {
return null;
}
@Override
public boolean isSingleton() {
return true;
}
}
com.study.elvinle.ioc.scan.TempDao:
@Component public class TempDao { public TempDao() { System.out.println("tempDao constructor"); } private String name = "tempDao"; public String getName() { return name; } public void setName(String name) { this.name = name; } }
com.study.elvinle.ioc.scan.TempScan:
// 這里使用 Configuration 也是能被掃描到的, 因為 Configuration中, 也有Component注解 //@Configuration @Component @ComponentScan("com.study.elvinle.ioc.fac") public class TempScan { }
com.study.elvinle.ioc.StartConfig:
@Configuration @ComponentScan("com.study.elvinle.ioc.scan") public class StartConfig { public StartConfig() { System.out.println("startConfig Constructor ... "); } }
從這里的掃描范圍來看, IndexFactoryBean 并不在這個配置的掃描范圍內.
測驗代碼:
public static void main(String[] args) { AnnotationConfigApplicationContext acac = new AnnotationConfigApplicationContext(StartConfig.class); System.out.println("---------------------"); IndexService index = (IndexService) acac.getBean("index"); index.getName(); IndexFactoryBean bean = (IndexFactoryBean) acac.getBean("&index"); bean.printf(); System.out.println("====================="); TempDao tempDao = acac.getBean(TempDao.class); System.out.println(tempDao.getName()); }
結果:

從這個結果看, 掃描發生了接力, 也就是說,
1. StartConfig 掃描的時候, 掃到了 TempScan
2. 決議 TempScan 的時候, 發現 TempScan 上面, 有@Component 和 ComponentScan 注解
3. 決議 TempScan的@ComponentScan注解, 再次進行掃描操作
4. TempScan 掃描到了 IndexFactoryBean, 然后對 IndexFactoryBean 進行決議
原始碼:
org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass
//region 處理 @ComponnentScan 注解 // Process any @ComponentScan annotations Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { for (AnnotationAttributes componentScan : componentScans) { // The config class is annotated with @ComponentScan -> perform the scan immediately //掃描普通類 //這里掃描出來所有 @Component //并且把掃描的出來的普通bean放到 map 當中 Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); // Check the set of scanned definitions for any further config classes and parse recursively if needed //檢查掃描出來的類當中是否還有configuration for (BeanDefinitionHolder holder : scannedBeanDefinitions) { BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition(); if (bdCand == null) { bdCand = holder.getBeanDefinition(); } //檢查掃描出來的類中, 是否還有加載了 @Configuration 的類, 如果有, 則接著遞回處理 if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { parse(bdCand.getBeanClassName(), holder.getBeanName()); } } } } //endregion
在spring原始碼里面, 會經常看到 遞回呼叫. 因為不確定性. 此方法里面, 對掃描出來的類, 還需要進行遞回處理.
因為被掃描出來的類, 可能并不是一個普通的類, 他也可能會加一些需要決議的注解, 如 @ComponentScan, @Import
進入 parse 方法看:
org.springframework.context.annotation.ComponentScanAnnotationParser#parse
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) { //掃描的時候, spring 自己new了一個ClassPathBeanDefinitionScanner來使用, 此處證實, 前面那個new出來的 reader , 確實不是給自己用的 //默認情況下, useDefaultFilters 是true,
//創建ClassPathBeanDefinitionScanner的時候, 會默認使用一個過濾器: AnnotationTypeFilter(Component.class) ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry, componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader); //BeanNameGenerator // 獲取 bean 的名字生成器, ComponentScan 默認的是 BeanNameGenerator, 也可以自定義覆寫 Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator"); boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass); scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator : BeanUtils.instantiateClass(generatorClass)); //web當中在來講 ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy"); if (scopedProxyMode != ScopedProxyMode.DEFAULT) { scanner.setScopedProxyMode(scopedProxyMode); } else { Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver"); scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass)); } scanner.setResourcePattern(componentScan.getString("resourcePattern")); //遍歷當中的過濾 for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) { for (TypeFilter typeFilter : typeFiltersFor(filter)) { scanner.addIncludeFilter(typeFilter); } } for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) { for (TypeFilter typeFilter : typeFiltersFor(filter)) { scanner.addExcludeFilter(typeFilter); } } //默認false boolean lazyInit = componentScan.getBoolean("lazyInit"); if (lazyInit) { scanner.getBeanDefinitionDefaults().setLazyInit(true); } Set<String> basePackages = new LinkedHashSet<>(); String[] basePackagesArray = componentScan.getStringArray("basePackages"); for (String pkg : basePackagesArray) { String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg), ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); Collections.addAll(basePackages, tokenized); } for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) { basePackages.add(ClassUtils.getPackageName(clazz)); } if (basePackages.isEmpty()) { basePackages.add(ClassUtils.getPackageName(declaringClass)); } scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) { @Override protected boolean matchClassName(String className) {
//排除自己 return declaringClass.equals(className); } }); return scanner.doScan(StringUtils.toStringArray(basePackages)); }
這個方法, 不用細看, 主要是設定一些屬性的默認值, 只是有幾個地方需要注意一下:
1. 這里new 了一個 ClassPathBeanDefinitionScanner 出來, 進行掃描的作業, 也印證了前面篇幅說的,
在創建 AnnotationConfigApplicationContext 的時候, 其建構式中, 創建的 scanner , 并不是給spring自己用的, 而是給開發人員使用的.
2. 在創建這里的 scanner 的時候, 默認了一個 AnnotationTypeFilter , 并且在創建他的時候, 默認給了一個 @Component,
然后將這個設定到了 IncludeFilter 屬性中.
Filter顧名思義, 就是過濾器, 這個關系到掃描出來的內容是否符合我們的要求
3. 這里還設定了一個匿名的 AbstractTypeHierarchyTraversingFilter 實作類, 并設定給了 ExcludeFilter 屬性
4. 真正的掃描作業是 doScan 來完成的.
接著來看 doScan 方法:
org.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan
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) { //掃描basePackage路徑下的java檔案, 并進行過濾, 獲取加載了 @Component 注解的部分 //符合條件的并把它轉成BeanDefinition型別 Set<BeanDefinition> candidates = findCandidateComponents(basePackage); for (BeanDefinition candidate : candidates) { //決議scope屬性 ScopeMetadata scopeMetadata = https://www.cnblogs.com/elvinle/p/this.scopeMetadataResolver.resolveScopeMetadata(candidate); candidate.setScope(scopeMetadata.getScopeName()); String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); if (candidate instanceof AbstractBeanDefinition) { //如果這個類是AbstractBeanDefinition的子類 //則為他設定默認值,比如lazy,init destory postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); } if (candidate instanceof AnnotatedBeanDefinition) { //檢查并且處理常用的注解 //這里的處理主要是指把常用注解的值設定到AnnotatedBeanDefinition當中 //當前前提是這個類必須是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); //將這個掃描到的 bd 注冊到 spring 容器中(this.beanDefinitionMap) registerBeanDefinition(definitionHolder, this.registry); } } } return beanDefinitions; }
findCandidateComponents 這個方法, 內部會呼叫 scanCandidateComponents 方法.
scanCandidateComponents
org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#scanCandidateComponents
private Set<BeanDefinition> scanCandidateComponents(String basePackage) { Set<BeanDefinition> candidates = new LinkedHashSet<>(); try { String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + '/' + this.resourcePattern; 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); 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); } } } 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; }
從這段代碼看, spring是通過掃描 java 檔案的方式, 來加載的, 然后根據規則進行轉換和過濾.
isCandidateComponent 方法, 如果能滿足, 就會為加載的資源來創建 bd .
所以主要看一下
org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#isCandidateComponent
方法.
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; }
這里的 excluderFilters 里面裝的, 就是前面的那個AbstractTypeHierarchyTraversingFilter 的匿名實作類
includeFilters 里面裝的, 就是前面的 AnnotationTypeFilter 類
AbstractTypeHierarchyTraversingFilter
這個匿名實作類, 對 match 進行了重寫覆寫, 判斷條件為:
declaringClass.equals(className)
主要就是為了將自己排除.
declaringClass 是方法傳入的類, 也就是被決議的類
className 是掃描到的類.
AnnotationTypeFilter
這個類中, 并沒有重寫match 方法, 但是重寫了 matchSlef 方法, 所以會先進父類中的 match 方法.
在其父類方法
org.springframework.core.type.filter.AbstractTypeHierarchyTraversingFilter#match
中, 呼叫了 matchSelf 方法, 這個方法就是 AnnotationTypeFilter 的方法了.
@Override protected boolean matchSelf(MetadataReader metadataReader) { AnnotationMetadata metadata = metadataReader.getAnnotationMetadata(); //這里判斷的其實就是 自己加了 指定的注解, 如 @Component, 或者 自己加的注解里面, 有指定的注解,
//如 @Configuration 中, 就有 @Component return metadata.hasAnnotation(this.annotationType.getName()) || (this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName())); }
還記的前面提到過, 在創建這個類的時候, 傳了一個 @Component 進來了, 所以這里判斷的其實是,
被掃描到的類, 是否直接或者間接的有 @Component 注解.
如果沒有這個注解, scanner 是會將這個類丟棄的.
看原始碼的時候, 有個技巧:
1. 很多方法是不需要看的, 先看主要方法, 不能鉆牛角尖
2. 看原始碼之前, 必須對要看的的功能, 起碼有個大致的了解, 否則會看的云里霧里, 不知其意.
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/131423.html
標籤:Java
上一篇:IDEA復制問題
下一篇:BeanFactory后置處理器 - ConfigurationClassPostProcessor - Import
