新聞中心
?一、學(xué)習(xí)指引
想成為禿頂?shù)馁Y深工程師,關(guān)于@ComponentScans注解與@ComponentScan注解,不能只停留在表面!

為棗莊等地區(qū)用戶提供了全套網(wǎng)頁設(shè)計制作服務(wù),及棗莊網(wǎng)站建設(shè)行業(yè)解決方案。主營業(yè)務(wù)為成都做網(wǎng)站、成都網(wǎng)站設(shè)計、棗莊網(wǎng)站設(shè)計,以傳統(tǒng)方式定制建設(shè)網(wǎng)站,并提供域名空間備案等一條龍服務(wù),秉承以專業(yè)、用心的態(tài)度為用戶提供真誠的服務(wù)。我們深信只要達(dá)到每一位用戶的要求,就會得到認(rèn)可,從而選擇與我們長期合作。這樣,我們也可以走得更遠(yuǎn)!
翻開Spring的源碼找到@ComponentScan注解的源碼,發(fā)現(xiàn)注解類上赫然標(biāo)注著Since: 3.1?字樣。也就是說,@ComponentScan注解是從Spring的3.1版本開始提供的。在@ComponentScan注解上,標(biāo)注了一個@Repeatable注解,@Repeatable注解的屬性值為ComponentScans.class。再次翻看下@ComponentScans注解的源碼,類上標(biāo)注著Since: 4.3字樣。也就是說,@ComponentScans注解是從Spring4.3版本開始提供的。@ComponentScans注解就相當(dāng)于是@ComponentScan注解的一個數(shù)組,在@ComponentScans注解中可以多次使用@ComponentScan注解來掃描不同的包路徑。
如果你只想做一個天天加班的CRUD的程序員,掌握上述的基本知識就夠了。CRUD操作不需要你對@ComponentScans注解與@ComponentScan注解有多么深入的了解。但是,如果你想對Spring的源碼有進(jìn)一步的了解和認(rèn)識,想熟悉Spring核心源碼的執(zhí)行流程,想成為一名合格的架構(gòu)師或技術(shù)專家,只了解上述@ComponentScans注解與@ComponentScan注解最基本的知識點是遠(yuǎn)遠(yuǎn)不夠的。
二、注解說明?
@ComponentScans注解與@ComponentScan注解的一點點說明!
@ComponentScans注解可以看作是@ComponentScan注解的一個數(shù)組,在@ComponentScans注解中可以多次標(biāo)注@ComponentScan注解。
@ComponentScan注解最核心的功能就是Spring IOC容器在刷新的時候會掃描對應(yīng)包下標(biāo)注了@Component注解、@Configuration注解、@Repository注解、@Service注解和@Controller等等注解的類,生成掃描到的類的Bean定義信息,整體流程與注冊ConfigurationClassPostProcessor類的Bean定義信息的流程基本一致,最終都會將其保存到BeanFactory中的beanDefinitionMap中。
2.1 注解源碼
本節(jié),主要是對@ComponentScans注解和@ComponentScan注解的源碼進(jìn)行簡單的剖析。
2.1.1 @ComponentScans注解源碼
源碼詳見:org.springframework.context.annotation.ComponentScans,如下所示。
/***
* @author Juergen Hoeller
* @since 4.3
* @see ComponentScan
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface ComponentScans {
ComponentScan[] value();
}
可以看到,@ComponentScans注解的源碼還是比較簡單的,在@ComponentScans注解中存在一個ComponentScan[]數(shù)組類型的value屬性,說明@ComponentScans注解的屬性可以存放一個@ComponentScan注解類型的數(shù)組,可以在ComponentScans注解中多次添加@ComponentScan注解。從@ComponentScans注解的源碼還可以看出,@ComponentScans注解從Spring 4.3版本開始提供。
2.1.2 @ComponentScan注解源碼
@ComponentScan注解的源碼是本章分析的重點內(nèi)容,@ComponentScan注解的源碼詳見:org.springframework.context.annotation.ComponentScan,如下所示。
/*
* @author Chris Beams
* @author Juergen Hoeller
* @author Sam Brannen
* @since 3.1
* @see Configuration
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
Class>[] basePackageClasses() default {};
Class extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
Class extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;
boolean useDefaultFilters() default true;
Filter[] includeFilters() default {};
Filter[] excludeFilters() default {};
boolean lazyInit() default false;
@Retention(RetentionPolicy.RUNTIME)
@Target({})
@interface Filter {
FilterType type() default FilterType.ANNOTATION;
@AliasFor("classes")
Class>[] value() default {};
@AliasFor("value")
Class>[] classes() default {};
String[] pattern() default {};
}
}
可以看到,Spring從3.1版本開始提供@ComponentScan注解,@ComponentScan注解中還有一個內(nèi)部注解@Filter。
@ComponentScan注解中的每個屬性的含義如下所示。
- value:作用同basePackages屬性,String[]數(shù)組類型,指定要掃描的包名。如果指定了要掃描的包名,則Spring會掃描指定的包及其子包下的所有類。
- basePackages:作用同value屬性,String[]數(shù)組類型,指定要掃描的包名。如果指定了要掃描的包名,則Spring會掃描指定的包及其子包下的所有類。
- basePackageClasses:Class>[]數(shù)組類型,指定要掃描的類的Class對象。
- nameGenerator:Class extends BeanNameGenerator>類型,指定掃描類時,向IOC注入Bean對象時的命名規(guī)則。
- scopeResolver:Class extends ScopeMetadataResolver>類型,掃描類時,用于處理并轉(zhuǎn)換符合條件的Bean的作用范圍。
- scopedProxy:ScopedProxyMode類型,指定生成Bean對象時的代理方式,默認(rèn)的代理方法是DEFAULT,也就是不使用代理。關(guān)于ScopedProxyMode的更多詳細(xì)的內(nèi)容,參見2.1.3節(jié)。
- resourcePattern:String類型,用于指定掃描的文件類型,默認(rèn)是掃描指定包下的**/*.class。
- useDefaultFilters:boolean類型,是否自動檢測@Component @Repository @Service @Controller注解,默認(rèn)是true。
- includeFilters:Filter[]數(shù)組類型,自定義組件掃描過濾規(guī)則,符合過濾規(guī)則的類的Bean定義信息會被注冊到IOC容器中。includeFilters表示只包含對應(yīng)的規(guī)則,當(dāng)使用includeFilters()來指定只包含哪些注解標(biāo)注的類時,需要禁用默認(rèn)的過濾規(guī)則,也就是需要將useDefaultFilters屬性設(shè)置為false。并且,除了符合過濾規(guī)則的類外,Spring內(nèi)置的如下名稱的類的Bean定義信息注冊到IOC容器時不受過濾規(guī)則限制,如下所示。
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
- excludeFilters:Filter[]數(shù)組類型,自定義組件掃描過濾規(guī)則,excludeFilters表示排除使用對應(yīng)的規(guī)則,符合過濾規(guī)則的類的Bean定義信息不會被注冊到IOC容器中。
- lazyInit:boolean類型,從Spring4.1版本開始提供,表示Spring掃描組件時是否采用懶加載 ,默認(rèn)false,表示不開啟懶加載。
- @Filter注解中的每個屬性的含義如下所示。
- type:FilterType類型,表示過濾規(guī)則的類型。關(guān)于FilterType的更多詳細(xì)的內(nèi)容,參見2.1.4節(jié)。
- value:Class>[]數(shù)組類型,過濾符合規(guī)則的類,作用同classes屬性。
- classes:Class>[]數(shù)組類型,過濾符合規(guī)則的類,作用同value屬性。
- pattern:如果FilterType取值為ASPECTJ,則此屬性表示ASPECTJ表達(dá)式。
2.1.3 ScopedProxyMode枚舉類源碼
ScopedProxyMode枚舉類表示Spring指定生成Bean對象時的代理方式,源碼詳見:org.springframework.context.annotation.ScopedProxyMode。
/*
* @author Mark Fisher
* @since 2.5
* @see ScopeMetadata
*/
public enum ScopedProxyMode {
DEFAULT,
NO,
INTERFACES,
TARGET_CLASS
}
ScopedProxyMode類是從Spring 2.5版本開始提供的枚舉類,每個屬性的含義如下所示。
- DEFAULT:默認(rèn)的代理方式,也就是不使用代理,除非在component-scan級別使用了不同的配置。
- NO:不使用代理。
- INTERFACES:基于JDK動態(tài)代理實現(xiàn)接口代理對象。
- TARGET_CLASS:基于CGLib動態(tài)代理創(chuàng)建類代理對象。
2.1.4 FilterType枚舉類源碼
FilterType枚舉類表示Spring掃描類時的過濾類型,源碼詳見:org.springframework.context.annotation.FilterType,如下所示。
/*
* @author Mark Fisher
* @author Juergen Hoeller
* @author Chris Beams
* @since 2.5
*/
public enum FilterType {
ANNOTATION,
ASSIGNABLE_TYPE,
ASPECTJ,
REGEX,
CUSTOM
}
FilterType類是Spring2.5版本開始提供的枚舉類,每個屬性的含義如下所示。
- ANNOTATION:按照注解進(jìn)行過濾。
- ASSIGNABLE_TYPE:按照給定的類型進(jìn)行過濾。
- ASPECTJ:按照ASPECTJ表達(dá)式進(jìn)行過濾。
- REGEX:按照正則表達(dá)式進(jìn)行過濾。
- CUSTOM:按照自定義規(guī)則進(jìn)行過濾,使用自定義過濾規(guī)則時,自定義的過濾器需要實現(xiàn)org.springframework.core.type.filter.TypeFilter接口。
在FilterType枚舉類中,ANNOTATION和ASSIGNABLE_TYPE是比較常用的,ASPECTJ和REGEX不太常用,如果FilterType枚舉類中的類型無法滿足日常開發(fā)需求時,可以通過實現(xiàn)org.springframework.core.type.filter.TypeFilter接口來自定義過濾規(guī)則,此時,將@Filter中的type屬性設(shè)置為FilterType.CUSTOM,classes屬性設(shè)置為自定義規(guī)則的類對應(yīng)的Class對象。
2.2 注解使用場景
使用Spring的注解開發(fā)應(yīng)用程序時,如果需要將標(biāo)注了Spring注解的類注入到IOC容器中,就需要使用@ComponentScan注解來掃描指定包下的類。同時,在Spring4.3版本開始,提供了@ComponentScans注解,在@ComponentScans注解中,支持配置多個@ComponentScan注解來掃描不同的包,配置不同的過濾規(guī)則。
三、使用案例?
整個案例來玩玩兒吧!
3.1 案例描述
使用自定義過濾規(guī)則實現(xiàn)Spring掃描指定包下的類時,使得名稱中含有 componentScanConfig 字符串的類符合過濾規(guī)則。
3.2 案例實現(xiàn)
整個案例實現(xiàn)的步驟總體如下所示。
(1)新建自定義過濾規(guī)則類ComponentScanFilter
ComponentScanFilter類的源碼詳見:spring-annotation-chapter-02工程下的io.binghe.spring.annotation.chapter02.componentscan.filter.ComponentScanFilter,如下所示。
public class ComponentScanFilter implements TypeFilter {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
//獲取當(dāng)前正在掃描的類的信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
//獲取當(dāng)前正在掃描的類名
String className = classMetadata.getClassName();
return className.contains("componentScanConfig");
}
}可以看到,自定義過濾規(guī)則ComponentScanFilter類實現(xiàn)了TypeFilter接口,并覆寫了match()方法,match()方法中的核心邏輯就是:如果類的名稱中含有componentScanConfig字符串,符合過濾規(guī)則,返回true,否則,返回false。
(2)新建配置類ComponentScanConfig
ComponentScanConfig類的源碼詳見:spring-annotation-chapter-02工程下的io.binghe.spring.annotation.chapter02.componentscan.config.ComponentScanConfig,如下所示。
@Configuration
@ComponentScan(value = "io.binghe.spring.annotation.chapter02", includeFilters = {
@Filter(type = FilterType.CUSTOM, classes = {ComponentScanFilter.class})
}, useDefaultFilters = false)
public class ComponentScanConfig {
}
可以看到,在ComponentScanConfig類上標(biāo)注了@Configuration注解,說明ComponentScanConfig類是Spring的配置類。在標(biāo)注的@ComponentScan注解中指定了要掃描的包名,使用只包含的過濾規(guī)則,并采用自定義過濾規(guī)則。
此時,需要注意的是,需要將是否使用默認(rèn)的過濾規(guī)則設(shè)置為false。
(3)新建測試類ComponentScanTest
ComponentScanTest類的源碼詳見:spring-annotation-chapter-02工程下的io.binghe.spring.annotation.chapter02.componentscan.ComponentScanTest,如下所示。
public class ComponentScanTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ComponentScanConfig.class);
String[] names = context.getBeanDefinitionNames();
Arrays.stream(names).forEach(System.out::println);
}
}可以看到,在ComponentScanTest類中,在AnnotationConfigApplicationContext類的構(gòu)造方法中傳入ComponentScanConfig類的Class對象創(chuàng)建IOC容器,并將其賦值給context局部變量。通過context局部變量的getBeanDefinitionNames()方法獲取所有的Bean定義名稱,隨后遍歷這些Bean定義名稱進(jìn)行打印。
3.3 案例測試
本案例中,在@ComponentScan注解中使用了includeFilters過濾規(guī)則,并且使用的是自定義過濾規(guī)則,符合過濾規(guī)則的是名稱中含有 componentScanConfig 字符串的類。另外,Spring中內(nèi)置的Processor類和Factory類的Bean定義信息注冊到IOC容器時,不受過濾規(guī)則限制。
運行ComponentScanTest類輸出的結(jié)果信息如下所示。
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
componentScanConfig
可以看到,從IOC容器中獲取的Bean的類定義信息的名稱中可以看出,除了名稱中包含componentScanConfig字符串的類符合過濾規(guī)則外,Spring內(nèi)置的Processor類和Factory類不受過濾規(guī)則限制,其類的Bean定義信息都注冊到了IOC容器中。
3.4 其他應(yīng)用案例
1.掃描時排除注解標(biāo)注的類
排除@Controller、@Service和@Repository注解,可以在配置類上通過@ComponentScan注解的excludeFilters()屬性實現(xiàn),如下所示。
@ComponentScan(value = "io.binghe.spring.annotation.chapter02", excludeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class, Repository.class})
})2.掃描時只包含注解標(biāo)注的類
可以使用ComponentScan注解類的includeFilters()屬性來指定Spring在進(jìn)行包掃描時,只包含哪些注解標(biāo)注的類。如果使用includeFilters()屬性來指定只包含哪些注解標(biāo)注的類時,需要禁用默認(rèn)的過濾規(guī)則。
例如,只包含@Controller注解標(biāo)注的類,可以在配置類上添加@ComponentScan注解,設(shè)置只包含@Controller注解標(biāo)注的類,并禁用默認(rèn)的過濾規(guī)則,如下所示。
@ComponentScan(value = "io.binghe.spring.annotation.chapter02", includeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
}, useDefaultFilters = false)3.重復(fù)注解
在Java8中@ComponentScan注解是一個重復(fù)注解,可以在一個配置類上重復(fù)使用這個注解,如下所示。
@ComponentScan(value = "io.binghe.spring.annotation.chapter02", includeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
}, useDefaultFilters = false)
@ComponentScan(value = "io.binghe.spring.annotation.chapter02", includeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = {Service.class})
}, useDefaultFilters = false)如果使用的是Java8之前的版本,就不能直接在配置類上寫多個@ComponentScan注解了。此時,可以在配置類上使用@ComponentScans注解,如下所示。
@ComponentScans(value = {
@ComponentScan(value = "io.binghe.spring.annotation.chapter02", includeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
}, useDefaultFilters = false),
@ComponentScan(value = "io.binghe.spring.annotation.chapter02", includeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = {Service.class})
}, useDefaultFilters = false)
})總結(jié):可以使用@ComponentScan注解來指定Spring掃描哪些包,可以使用excludeFilters()指定掃描時排除哪些組件,也可以使用includeFilters()指定掃描時只包含哪些組件。當(dāng)使用includeFilters()指定只包含哪些組件時,需要禁用默認(rèn)的過濾規(guī)則。
4.掃描時按照注解進(jìn)行過濾
使用@ComponentScan注解進(jìn)行包掃描時,按照注解只包含標(biāo)注了@Controller注解的組件,如下所示。
@ComponentScan(value = "io.binghe.spring.annotation.chapter02", includeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
}, useDefaultFilters = false)5.掃描時按照指定的類型進(jìn)行過濾
使用@ComponentScan注解進(jìn)行包掃描時,按照給定的類型只包含DemoService類(接口)或其子類(實現(xiàn)類或子接口)的組件,如下所示。
@ComponentScan(value = "io.binghe.spring.annotation.chapter02", includeFilters = {
@Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {DemoService.class})
}, useDefaultFilters = false)此時,只要是DemoService類型的組件,都會被加載到容器中。也就是說:當(dāng)DemoService是一個Java類時,DemoService類及其子類都會被加載到Spring容器中;當(dāng)DemoService是一個接口時,其子接口或?qū)崿F(xiàn)類都會被加載到Spring容器中。
6.掃描時按照ASPECTJ表達(dá)式進(jìn)行過濾
使用@ComponentScan注解進(jìn)行包掃描時,按照ASPECTJ表達(dá)式進(jìn)行過濾,如下所示。
@ComponentScan(value = "io.binghe.spring.annotation.chapter02", includeFilters = {
@Filter(type = FilterType.ASPECTJ, classes = {AspectJTypeFilter.class})
}, useDefaultFilters = false)其中,AspectJTypeFilter類就是自定義的ASPECTJ表達(dá)式的過濾器類。
7.掃描時按照正則表達(dá)式進(jìn)行過濾
使用@ComponentScan注解進(jìn)行包掃描時,按照正則表達(dá)式進(jìn)行過濾,如下所示。
@ComponentScan(value = "io.binghe.spring.annotation.chapter02", includeFilters = {
@Filter(type = FilterType.REGEX, classes = {RegexPatternTypeFilter.class})
}, useDefaultFilters = false)其中,RegexPatternTypeFilter類就是自定義的正則表達(dá)式的過濾器類。
8.掃描時按照自定義規(guī)則進(jìn)行過濾
如果實現(xiàn)自定義規(guī)則進(jìn)行過濾時,自定義規(guī)則的類必須是org.springframework.core.type.filter.TypeFilter接口的實現(xiàn)類。
例如,按照自定義規(guī)則進(jìn)行過濾,首先,需要創(chuàng)建一個org.springframework.core.type.filter.TypeFilter接口的實現(xiàn)類BingheTypeFilter,如下所示。
public class BingheTypeFilter implements TypeFilter {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
return false;
}
}當(dāng)實現(xiàn)TypeFilter接口時,需要實現(xiàn)TypeFilter接口中的match()方法,match()方法的返回值為boolean類型。當(dāng)返回true時,表示符合過濾規(guī)則,會將類的Bean定義信息注冊到IOC容器中;當(dāng)返回false時,表示不符合過濾規(guī)則,對應(yīng)的類的Bean定義信息不會注冊到IOC容器中。
接下來,使用@ComponentScan注解進(jìn)行如下配置。
@ComponentScan(value = "io.binghe.spring.annotation.chapter02", includeFilters = {
@Filter(type = FilterType.CUSTOM, classes = {BingheTypeFilter.class})
}, useDefaultFilters = false)
四、源碼時序圖?
結(jié)合時序圖理解源碼會事半功倍,你覺得呢?
本節(jié),就以源碼時序圖的方式,直觀的感受下@ComponentScans注解與@ComponentScan注解在Spring源碼層面的執(zhí)行流程。@ComponentScans注解與@ComponentScan注解在Spring源碼層面的執(zhí)行流程如圖2-1~2-3所示。
圖2-1
圖2-2
圖2-3
由圖2-1~2-3可以看出,解析@ComponentScans注解與@ComponentScan注解在Spring源碼中的執(zhí)行流程,會涉及到ComponentScanTest類、AnnotationConfigApplicationContext類、AbstractApplicationContext類、PostProcessorRegistrationDelegate類、ConfigurationClassPostProcessor類、ConfigurationClassParser類、ComponentScanAnnotationParser類、ClassPathBeanDefinitionScanner類、ClassPathScanningCandidateComponentProvider類、BeanDefinitionReaderUtils類和DefaultListableBeanFactory類等。具體的源碼執(zhí)行細(xì)節(jié)參見源碼解析部分。
五、源碼解析?
源碼時序圖整清楚了,那就整源碼解析唄!
@ComponentScans注解與@ComponentScan注解在Spring源碼中的執(zhí)行流程,結(jié)合源碼執(zhí)行的時序圖,會理解的更加深刻。
(1)運行案例程序啟動類
案例程序啟動類源碼詳見:spring-annotation-chapter-02工程下的io.binghe.spring.annotation.chapter02.componentscan.ComponentScanTest,運行ComponentScanTest類的main()方法。
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ComponentScanConfig.class);
String[] names = context.getBeanDefinitionNames();
Arrays.stream(names).forEach(System.out::println);
}可以看到,在ComponentScanTest類的main()方法中調(diào)用了AnnotationConfigApplicationContext類的構(gòu)造方法,并傳入了ComponentScanConfig類的Class對象來創(chuàng)建IOC容器。接下來,會進(jìn)入AnnotationConfigApplicationContext類的構(gòu)造方法。
(2)解析AnnotationConfigApplicationContext類的AnnotationConfigApplicationContext(Class>... componentClasses)構(gòu)造方法
源碼詳見:org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(Class>... componentClasses)。
public AnnotationConfigApplicationContext(Class>... componentClasses) {
this();
register(componentClasses);
refresh();
}可以看到,在上述構(gòu)造方法中,調(diào)用了refresh()方法來刷新IOC容器。
(3)解析AbstractApplicationContext類的refresh()方法
源碼詳見:org.springframework.context.support.AbstractApplicationContext#refresh()。
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//############省略其他代碼##############
try {
//############省略其他代碼##############
invokeBeanFactoryPostProcessors(beanFactory);
//############省略其他代碼##############
}catch (BeansException ex) {
//############省略其他代碼##############
}finally {
//############省略其他代碼##############
}
}
}
refresh()方法是Spring中一個非常重要的方法,很多重要的功能和特性都是通過refresh()方法進(jìn)行注入的??梢钥吹?,在refresh()方法中,調(diào)用了invokeBeanFactoryPostProcessors()方法。
(4)解析AbstractApplicationContext類的invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory)方法
源碼詳見:org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory)。
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
if (!NativeDetector.inNativeImage() && beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
}可以看到,在AbstractApplicationContext類的invokeBeanFactoryPostProcessors()方法中調(diào)用了PostProcessorRegistrationDelegate類的invokeBeanFactoryPostProcessors()方法。
(5)解析PostProcessorRegistrationDelegate類的invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, ListbeanFactoryPostProcessors)方法
源碼詳見:org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, ListbeanFactoryPostProcessors)。
由于方法的源碼比較長,這里,只關(guān)注當(dāng)前最核心的邏輯,如下所示。
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, ListbeanFactoryPostProcessors) {
//############省略其他代碼##############
ListcurrentRegistryProcessors = new ArrayList<>();
// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
currentRegistryProcessors.clear();
// Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
currentRegistryProcessors.clear();
// Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.
boolean reiterate = true;
while (reiterate) {
reiterate = false;
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
reiterate = true;
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
currentRegistryProcessors.clear();
}
//############省略其他代碼##############
}
可以看到,在PostProcessorRegistrationDelegate類的invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, ListbeanFactoryPostProcessors)方法中,BeanDefinitionRegistryPostProcessor的實現(xiàn)類在執(zhí)行邏輯上會有先后順序,并且最終都會調(diào)用invokeBeanDefinitionRegistryPostProcessors()方法。
(6)解析PostProcessorRegistrationDelegate類的invokeBeanDefinitionRegistryPostProcessors(Collection extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry, ApplicationStartup applicationStartup)方法
源碼詳見:org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanDefinitionRegistryPostProcessors(Collection extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry, ApplicationStartup applicationStartup)。
private static void invokeBeanDefinitionRegistryPostProcessors(
Collection extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry, ApplicationStartup applicationStartup) {
for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
StartupStep postProcessBeanDefRegistry = applicationStartup.start("spring.context.beandef-registry.post-process")
.tag("postProcessor", postProcessor::toString);
postProcessor.postProcessBeanDefinitionRegistry(registry);
postProcessBeanDefRegistry.end();
}
}
可以看到,在invokeBeanDefinitionRegistryPostProcessors()方法中,會循環(huán)遍歷postProcessors集合中的每個元素,調(diào)用postProcessBeanDefinitionRegistry()方法注冊Bean的定義信息。
(7)解析ConfigurationClassPostProcessor類的postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)方法
源碼詳見:org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)。
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
//##########省略其他代碼###################
processConfigBeanDefinitions(registry);
}
可以看到,在postProcessBeanDefinitionRegistry()方法中,會調(diào)用processConfigBeanDefinitions()方法。
(8)解析ConfigurationClassPostProcessor類的processConfigBeanDefinitions(BeanDefinitionRegistry registry)方法
源碼詳見:org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions(BeanDefinitionRegistry registry)。
這里,重點關(guān)注方法中的如下邏輯。
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
//############省略其他代碼#################
// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set candidates = new LinkedHashSet<>(configCandidates);
Set alreadyParsed = new HashSet<>(configCandidates.size());
do {
StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");
parser.parse(candidates);
parser.validate();
//############省略其他代碼#################
}
while (!candidates.isEmpty());
//############省略其他代碼#################
} 可以看到,在processConfigBeanDefinitions()方法中,創(chuàng)建了一個ConfigurationClassParser類型的對象parser,并且調(diào)用了parser的parse()方法來解析類的配置信息。
(9)解析ConfigurationClassParser類的parse(SetconfigCandidates)方法
源碼詳見:org.springframework.context.annotation.ConfigurationClassParser#parse(SetconfigCandidates)。
public void parse(SetconfigCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
本文標(biāo)題:一篇了解@ComponentScan注解
文章位置:http://www.dlmjj.cn/article/ccsppji.html


咨詢
建站咨詢
