上一篇:Spring5 源碼分析-容器刷新-@PropertySourcejava
@ComponentScan能夠完成對指定路徑下的類文件掃描,將符合Spring規則(@Component @Service @Controller @Repository @Named(JSR-250))或者自定義規則(自定義的FilterType)的類定義註冊到容器中(即將各個類的BeanDefinition添加到DefaultListableBeanFactory.beanDefinitionMaps當中)。能夠用@ComponentScan的屬性指定多個代理模式、是否懶加載、ResourcePattern、includeFilters 、 excludeFitlersapp
@ComponentScan屬性ide
value和basePackages效果同樣,都是用來指定掃描的包路徑源碼分析
basePackageClasses:添加指定class所在包路徑做爲掃描包路徑post
nameGenerator:指定beanName名稱生成器,通常不會去修改它測試
scopeResolver:指定Scope元數據提取器,通常不會去修改它this
scopedProxy:指定Bean的代理模式,默認爲DEFAULT,其效果和NO同樣,還有TARGET_CLASS(CGLIB)和INTERFACES(JDK動態代理)lua
resoucePattern:過濾包路徑,好比包掃描路徑是com.jv.scan,你能夠設置一個pattern,將com.jv.scan.inner目錄過濾掉,結合includeFilters和excludeFilters能夠實現更靈活的包掃描spa
useDefaultFilters:默認爲true,誰也不會去改這個值,由於它是Spring支持@Component註解的關鍵點.net
lazyInit:默認爲false,若是你想全局設置爲懶加載,則能夠設置爲true
includeFilters:符合Filter指定規則的類會被掃描
excludeFilters:符合Filter指定規則的類不會被掃描
注:對於能夠在Bean上面單獨設置的屬性,若是針對某一個類單獨設置了,在@ComponentScan上設置的會被覆蓋掉。好比lazyInit和scopeProxyMode
這個簡單,任何一個Spring項目都會在配置類上添加@ComponentScan註解,至於上面的各個屬性也很方便測試,不復雜。因爲時間關係,沒有將測試的代碼一個一個整理並貼出來。裏面可能會比較經常使用的就是使用value,basePackages,basePackageClasses去指定掃描包路徑,還有設置includeFilters和excludeFilters用以控制自定義的掃描規則
注:若是沒有指定任何的包掃描路徑,則使用的是配置類所在包的路徑
方法按照以下順序refresh()->invokeBeanFactoryPostProcessors()->ConfigurationClassPostProcessor->postProcessBeanDefinitionRegistry()-ConfigurationClassParse.doProcessConfigurationClass()...ComponentScanAnnotationParse.parse()...ClassPathBeanDefinitionScanner.doScan
在ConfigurationClassParse.doProcessConfigurationClass方法中的代碼段
// Process any @ComponentScan annotations 處理@ComponentScan註解,完成符合Spring規則類定義註冊 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 當即執行掃描 //不會掃描到本身(@Configuration修飾了的類) 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(); } if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { //若是掃描的包路徑下還有被@Configuration修飾的類,則遞歸進行處理 parse(bdCand.getBeanClassName(), holder.getBeanName()); } } } }
ComponentScanAnnotationParse.parse方法
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) { /** * 這個初始化包含了兩個很是重要的功能 * 1.添加默認的@Component @Named AnnotationFilterType,是Spring能夠掃描@Component @Service @Controller @Configuration的關鍵 * 2.設置參數對象environment */ ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry, componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader); Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator"); boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass); scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator : BeanUtils.instantiateClass(generatorClass)); //默認不使用代理,能夠設置爲JDK的動態代理或者基於CGLIB的動態代理 ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy"); if (scopedProxyMode != ScopedProxyMode.DEFAULT) { scanner.setScopedProxyMode(scopedProxyMode); } else { //ScopeMetadataResolver 從BeanDefinition中將類的Scope元數據提取出來並返回 Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver"); scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass)); } //獲取類文件過濾模式,默認是處理全部類路徑下的全部class文件,能夠自定義pattern只讓某一個目錄下的class文件有效 scanner.setResourcePattern(componentScan.getString("resourcePattern")); //添加includeFilter過濾器,Spring會處理符合過濾器規則的class for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) { for (TypeFilter typeFilter : typeFiltersFor(filter)) { scanner.addIncludeFilter(typeFilter); } } //添加excludeFilter過濾器,Spring會排出符合過濾器規則的class for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) { for (TypeFilter typeFilter : typeFiltersFor(filter)) { scanner.addExcludeFilter(typeFilter); } } //全部的class的BeanDefinition的lazyInit都會被設置爲true,意思就是全部的都會在調用getBean的時候纔會被實例化 boolean lazyInit = componentScan.getBoolean("lazyInit"); if (lazyInit) { scanner.getBeanDefinitionDefaults().setLazyInit(true); } Set<String> basePackages = new LinkedHashSet<>(); String[] basePackagesArray = componentScan.getStringArray("basePackages"); //添加掃描包路徑-由basePackages指定的相對路徑 for (String pkg : basePackagesArray) { String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg), ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); Collections.addAll(basePackages, tokenized); } //添加掃描包路徑-由basePackageClasses類所在的包路徑 for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) { basePackages.add(ClassUtils.getPackageName(clazz)); } //若是前面二者都沒有指定,則使用當前處理類的所在包路徑。。。估計這也是爲何SpringBoot默認是掃描的啓動類所在目錄的緣由 if (basePackages.isEmpty()) { basePackages.add(ClassUtils.getPackageName(declaringClass)); } //添加一個默認ExcludeFilter,用於再也不二次處理配置類 scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) { @Override protected boolean matchClassName(String className) { return declaringClass.equals(className); } }); //掃描 return scanner.doScan(StringUtils.toStringArray(basePackages)); }
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) { //爲被掃描到的全部類生成BeanDefinition Set<BeanDefinition> candidates = findCandidateComponents(basePackage); //遍歷全部的候選者並進行處理最終註冊到容器中 for (BeanDefinition candidate : candidates) { //提取Scope元數據 ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); candidate.setScope(scopeMetadata.getScopeName()); //生成Spring規則的beanName String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); if (candidate instanceof AbstractBeanDefinition) { //設置全局默認值,好比lazyInit=false(默認爲false,能夠在@ComponentScan中設置爲true),autowireMode,InitMethod等 postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); } if (candidate instanceof AnnotatedBeanDefinition) { AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); } if (checkCandidate(beanName, candidate)) { BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); /** * 這裏很重要,建立BeanDefinition的ProxyDefinition * 1.若是ScopedProxyMode是DEFAULT或者NO, 則不會建立ProxyDefinition * 2.若是ScopedProxyMode不是TARGET_CLASS,則建立的ProxyDefinition會用於JDK動態代理 * 3.若是ScopedProxyMode是TARGET_CLASS,則建立的ProxyDefinition會用於CGLIB動態代理 */ definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); beanDefinitions.add(definitionHolder); //註冊BeanDefinition registerBeanDefinition(definitionHolder, this.registry); } } } return beanDefinitions; }