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

為棗莊等地區(qū)用戶提供了全套網(wǎng)頁(yè)設(shè)計(jì)制作服務(wù),及棗莊網(wǎng)站建設(shè)行業(yè)解決方案。主營(yíng)業(yè)務(wù)為成都做網(wǎng)站、成都網(wǎng)站設(shè)計(jì)、棗莊網(wǎng)站設(shè)計(jì),以傳統(tǒng)方式定制建設(shè)網(wǎng)站,并提供域名空間備案等一條龍服務(wù),秉承以專(zhuān)業(yè)、用心的態(tài)度為用戶提供真誠(chéng)的服務(wù)。我們深信只要達(dá)到每一位用戶的要求,就會(huì)得到認(rèn)可,從而選擇與我們長(zhǎng)期合作。這樣,我們也可以走得更遠(yuǎn)!
翻開(kāi)Spring的源碼找到@ComponentScan注解的源碼,發(fā)現(xiàn)注解類(lèi)上赫然標(biāo)注著Since: 3.1?字樣。也就是說(shuō),@ComponentScan注解是從Spring的3.1版本開(kāi)始提供的。在@ComponentScan注解上,標(biāo)注了一個(gè)@Repeatable注解,@Repeatable注解的屬性值為ComponentScans.class。再次翻看下@ComponentScans注解的源碼,類(lèi)上標(biāo)注著Since: 4.3字樣。也就是說(shuō),@ComponentScans注解是從Spring4.3版本開(kāi)始提供的。@ComponentScans注解就相當(dāng)于是@ComponentScan注解的一個(gè)數(shù)組,在@ComponentScans注解中可以多次使用@ComponentScan注解來(lái)掃描不同的包路徑。
如果你只想做一個(gè)天天加班的CRUD的程序員,掌握上述的基本知識(shí)就夠了。CRUD操作不需要你對(duì)@ComponentScans注解與@ComponentScan注解有多么深入的了解。但是,如果你想對(duì)Spring的源碼有進(jìn)一步的了解和認(rèn)識(shí),想熟悉Spring核心源碼的執(zhí)行流程,想成為一名合格的架構(gòu)師或技術(shù)專(zhuān)家,只了解上述@ComponentScans注解與@ComponentScan注解最基本的知識(shí)點(diǎn)是遠(yuǎn)遠(yuǎn)不夠的。
二、注解說(shuō)明?
@ComponentScans注解與@ComponentScan注解的一點(diǎn)點(diǎn)說(shuō)明!
@ComponentScans注解可以看作是@ComponentScan注解的一個(gè)數(shù)組,在@ComponentScans注解中可以多次標(biāo)注@ComponentScan注解。
@ComponentScan注解最核心的功能就是Spring IOC容器在刷新的時(shí)候會(huì)掃描對(duì)應(yīng)包下標(biāo)注了@Component注解、@Configuration注解、@Repository注解、@Service注解和@Controller等等注解的類(lèi),生成掃描到的類(lèi)的Bean定義信息,整體流程與注冊(cè)ConfigurationClassPostProcessor類(lèi)的Bean定義信息的流程基本一致,最終都會(huì)將其保存到BeanFactory中的beanDefinitionMap中。
2.1 注解源碼
本節(jié),主要是對(duì)@ComponentScans注解和@ComponentScan注解的源碼進(jìn)行簡(jiǎn)單的剖析。
2.1.1 @ComponentScans注解源碼
源碼詳見(jiàn):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注解的源碼還是比較簡(jiǎn)單的,在@ComponentScans注解中存在一個(gè)ComponentScan[]數(shù)組類(lèi)型的value屬性,說(shuō)明@ComponentScans注解的屬性可以存放一個(gè)@ComponentScan注解類(lèi)型的數(shù)組,可以在ComponentScans注解中多次添加@ComponentScan注解。從@ComponentScans注解的源碼還可以看出,@ComponentScans注解從Spring 4.3版本開(kāi)始提供。
2.1.2 @ComponentScan注解源碼
@ComponentScan注解的源碼是本章分析的重點(diǎn)內(nèi)容,@ComponentScan注解的源碼詳見(jiàn):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版本開(kāi)始提供@ComponentScan注解,@ComponentScan注解中還有一個(gè)內(nèi)部注解@Filter。
@ComponentScan注解中的每個(gè)屬性的含義如下所示。
- value:作用同basePackages屬性,String[]數(shù)組類(lèi)型,指定要掃描的包名。如果指定了要掃描的包名,則Spring會(huì)掃描指定的包及其子包下的所有類(lèi)。
- basePackages:作用同value屬性,String[]數(shù)組類(lèi)型,指定要掃描的包名。如果指定了要掃描的包名,則Spring會(huì)掃描指定的包及其子包下的所有類(lèi)。
- basePackageClasses:Class>[]數(shù)組類(lèi)型,指定要掃描的類(lèi)的Class對(duì)象。
- nameGenerator:Class extends BeanNameGenerator>類(lèi)型,指定掃描類(lèi)時(shí),向IOC注入Bean對(duì)象時(shí)的命名規(guī)則。
- scopeResolver:Class extends ScopeMetadataResolver>類(lèi)型,掃描類(lèi)時(shí),用于處理并轉(zhuǎn)換符合條件的Bean的作用范圍。
- scopedProxy:ScopedProxyMode類(lèi)型,指定生成Bean對(duì)象時(shí)的代理方式,默認(rèn)的代理方法是DEFAULT,也就是不使用代理。關(guān)于ScopedProxyMode的更多詳細(xì)的內(nèi)容,參見(jiàn)2.1.3節(jié)。
- resourcePattern:String類(lèi)型,用于指定掃描的文件類(lèi)型,默認(rèn)是掃描指定包下的**/*.class。
- useDefaultFilters:boolean類(lèi)型,是否自動(dòng)檢測(cè)@Component @Repository @Service @Controller注解,默認(rèn)是true。
- includeFilters:Filter[]數(shù)組類(lèi)型,自定義組件掃描過(guò)濾規(guī)則,符合過(guò)濾規(guī)則的類(lèi)的Bean定義信息會(huì)被注冊(cè)到IOC容器中。includeFilters表示只包含對(duì)應(yīng)的規(guī)則,當(dāng)使用includeFilters()來(lái)指定只包含哪些注解標(biāo)注的類(lèi)時(shí),需要禁用默認(rèn)的過(guò)濾規(guī)則,也就是需要將useDefaultFilters屬性設(shè)置為false。并且,除了符合過(guò)濾規(guī)則的類(lèi)外,Spring內(nèi)置的如下名稱(chēng)的類(lèi)的Bean定義信息注冊(cè)到IOC容器時(shí)不受過(guò)濾規(guī)則限制,如下所示。
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
- excludeFilters:Filter[]數(shù)組類(lèi)型,自定義組件掃描過(guò)濾規(guī)則,excludeFilters表示排除使用對(duì)應(yīng)的規(guī)則,符合過(guò)濾規(guī)則的類(lèi)的Bean定義信息不會(huì)被注冊(cè)到IOC容器中。
- lazyInit:boolean類(lèi)型,從Spring4.1版本開(kāi)始提供,表示Spring掃描組件時(shí)是否采用懶加載 ,默認(rèn)false,表示不開(kāi)啟懶加載。
- @Filter注解中的每個(gè)屬性的含義如下所示。
- type:FilterType類(lèi)型,表示過(guò)濾規(guī)則的類(lèi)型。關(guān)于FilterType的更多詳細(xì)的內(nèi)容,參見(jiàn)2.1.4節(jié)。
- value:Class>[]數(shù)組類(lèi)型,過(guò)濾符合規(guī)則的類(lèi),作用同classes屬性。
- classes:Class>[]數(shù)組類(lèi)型,過(guò)濾符合規(guī)則的類(lèi),作用同value屬性。
- pattern:如果FilterType取值為ASPECTJ,則此屬性表示ASPECTJ表達(dá)式。
2.1.3 ScopedProxyMode枚舉類(lèi)源碼
ScopedProxyMode枚舉類(lèi)表示Spring指定生成Bean對(duì)象時(shí)的代理方式,源碼詳見(jiàn):org.springframework.context.annotation.ScopedProxyMode。
/*
* @author Mark Fisher
* @since 2.5
* @see ScopeMetadata
*/
public enum ScopedProxyMode {
DEFAULT,
NO,
INTERFACES,
TARGET_CLASS
}
ScopedProxyMode類(lèi)是從Spring 2.5版本開(kāi)始提供的枚舉類(lèi),每個(gè)屬性的含義如下所示。
- DEFAULT:默認(rèn)的代理方式,也就是不使用代理,除非在component-scan級(jí)別使用了不同的配置。
- NO:不使用代理。
- INTERFACES:基于JDK動(dòng)態(tài)代理實(shí)現(xiàn)接口代理對(duì)象。
- TARGET_CLASS:基于CGLib動(dòng)態(tài)代理創(chuàng)建類(lèi)代理對(duì)象。
2.1.4 FilterType枚舉類(lèi)源碼
FilterType枚舉類(lèi)表示Spring掃描類(lèi)時(shí)的過(guò)濾類(lèi)型,源碼詳見(jiàn):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類(lèi)是Spring2.5版本開(kāi)始提供的枚舉類(lèi),每個(gè)屬性的含義如下所示。
- ANNOTATION:按照注解進(jìn)行過(guò)濾。
- ASSIGNABLE_TYPE:按照給定的類(lèi)型進(jìn)行過(guò)濾。
- ASPECTJ:按照ASPECTJ表達(dá)式進(jìn)行過(guò)濾。
- REGEX:按照正則表達(dá)式進(jìn)行過(guò)濾。
- CUSTOM:按照自定義規(guī)則進(jìn)行過(guò)濾,使用自定義過(guò)濾規(guī)則時(shí),自定義的過(guò)濾器需要實(shí)現(xiàn)org.springframework.core.type.filter.TypeFilter接口。
在FilterType枚舉類(lèi)中,ANNOTATION和ASSIGNABLE_TYPE是比較常用的,ASPECTJ和REGEX不太常用,如果FilterType枚舉類(lèi)中的類(lèi)型無(wú)法滿足日常開(kāi)發(fā)需求時(shí),可以通過(guò)實(shí)現(xiàn)org.springframework.core.type.filter.TypeFilter接口來(lái)自定義過(guò)濾規(guī)則,此時(shí),將@Filter中的type屬性設(shè)置為FilterType.CUSTOM,classes屬性設(shè)置為自定義規(guī)則的類(lèi)對(duì)應(yīng)的Class對(duì)象。
2.2 注解使用場(chǎng)景
使用Spring的注解開(kāi)發(fā)應(yīng)用程序時(shí),如果需要將標(biāo)注了Spring注解的類(lèi)注入到IOC容器中,就需要使用@ComponentScan注解來(lái)掃描指定包下的類(lèi)。同時(shí),在Spring4.3版本開(kāi)始,提供了@ComponentScans注解,在@ComponentScans注解中,支持配置多個(gè)@ComponentScan注解來(lái)掃描不同的包,配置不同的過(guò)濾規(guī)則。
三、使用案例?
整個(gè)案例來(lái)玩玩兒吧!
3.1 案例描述
使用自定義過(guò)濾規(guī)則實(shí)現(xiàn)Spring掃描指定包下的類(lèi)時(shí),使得名稱(chēng)中含有 componentScanConfig 字符串的類(lèi)符合過(guò)濾規(guī)則。
3.2 案例實(shí)現(xiàn)
整個(gè)案例實(shí)現(xiàn)的步驟總體如下所示。
(1)新建自定義過(guò)濾規(guī)則類(lèi)ComponentScanFilter
ComponentScanFilter類(lèi)的源碼詳見(jiàn):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)前正在掃描的類(lèi)的信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
//獲取當(dāng)前正在掃描的類(lèi)名
String className = classMetadata.getClassName();
return className.contains("componentScanConfig");
}
}可以看到,自定義過(guò)濾規(guī)則ComponentScanFilter類(lèi)實(shí)現(xiàn)了TypeFilter接口,并覆寫(xiě)了match()方法,match()方法中的核心邏輯就是:如果類(lèi)的名稱(chēng)中含有componentScanConfig字符串,符合過(guò)濾規(guī)則,返回true,否則,返回false。
(2)新建配置類(lèi)ComponentScanConfig
ComponentScanConfig類(lèi)的源碼詳見(jiàn):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類(lèi)上標(biāo)注了@Configuration注解,說(shuō)明ComponentScanConfig類(lèi)是Spring的配置類(lèi)。在標(biāo)注的@ComponentScan注解中指定了要掃描的包名,使用只包含的過(guò)濾規(guī)則,并采用自定義過(guò)濾規(guī)則。
此時(shí),需要注意的是,需要將是否使用默認(rèn)的過(guò)濾規(guī)則設(shè)置為false。
(3)新建測(cè)試類(lèi)ComponentScanTest
ComponentScanTest類(lèi)的源碼詳見(jiàn):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類(lèi)中,在AnnotationConfigApplicationContext類(lèi)的構(gòu)造方法中傳入ComponentScanConfig類(lèi)的Class對(duì)象創(chuàng)建IOC容器,并將其賦值給context局部變量。通過(guò)context局部變量的getBeanDefinitionNames()方法獲取所有的Bean定義名稱(chēng),隨后遍歷這些Bean定義名稱(chēng)進(jìn)行打印。
3.3 案例測(cè)試
本案例中,在@ComponentScan注解中使用了includeFilters過(guò)濾規(guī)則,并且使用的是自定義過(guò)濾規(guī)則,符合過(guò)濾規(guī)則的是名稱(chēng)中含有 componentScanConfig 字符串的類(lèi)。另外,Spring中內(nèi)置的Processor類(lèi)和Factory類(lèi)的Bean定義信息注冊(cè)到IOC容器時(shí),不受過(guò)濾規(guī)則限制。
運(yùn)行ComponentScanTest類(lèi)輸出的結(jié)果信息如下所示。
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
componentScanConfig
可以看到,從IOC容器中獲取的Bean的類(lèi)定義信息的名稱(chēng)中可以看出,除了名稱(chēng)中包含componentScanConfig字符串的類(lèi)符合過(guò)濾規(guī)則外,Spring內(nèi)置的Processor類(lèi)和Factory類(lèi)不受過(guò)濾規(guī)則限制,其類(lèi)的Bean定義信息都注冊(cè)到了IOC容器中。
3.4 其他應(yīng)用案例
1.掃描時(shí)排除注解標(biāo)注的類(lèi)
排除@Controller、@Service和@Repository注解,可以在配置類(lèi)上通過(guò)@ComponentScan注解的excludeFilters()屬性實(shí)現(xiàn),如下所示。
@ComponentScan(value = "io.binghe.spring.annotation.chapter02", excludeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class, Repository.class})
})2.掃描時(shí)只包含注解標(biāo)注的類(lèi)
可以使用ComponentScan注解類(lèi)的includeFilters()屬性來(lái)指定Spring在進(jìn)行包掃描時(shí),只包含哪些注解標(biāo)注的類(lèi)。如果使用includeFilters()屬性來(lái)指定只包含哪些注解標(biāo)注的類(lèi)時(shí),需要禁用默認(rèn)的過(guò)濾規(guī)則。
例如,只包含@Controller注解標(biāo)注的類(lèi),可以在配置類(lèi)上添加@ComponentScan注解,設(shè)置只包含@Controller注解標(biāo)注的類(lèi),并禁用默認(rèn)的過(guò)濾規(guī)則,如下所示。
@ComponentScan(value = "io.binghe.spring.annotation.chapter02", includeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
}, useDefaultFilters = false)3.重復(fù)注解
在Java8中@ComponentScan注解是一個(gè)重復(fù)注解,可以在一個(gè)配置類(lèi)上重復(fù)使用這個(gè)注解,如下所示。
@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之前的版本,就不能直接在配置類(lèi)上寫(xiě)多個(gè)@ComponentScan注解了。此時(shí),可以在配置類(lèi)上使用@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注解來(lái)指定Spring掃描哪些包,可以使用excludeFilters()指定掃描時(shí)排除哪些組件,也可以使用includeFilters()指定掃描時(shí)只包含哪些組件。當(dāng)使用includeFilters()指定只包含哪些組件時(shí),需要禁用默認(rèn)的過(guò)濾規(guī)則。
4.掃描時(shí)按照注解進(jìn)行過(guò)濾
使用@ComponentScan注解進(jìn)行包掃描時(shí),按照注解只包含標(biāo)注了@Controller注解的組件,如下所示。
@ComponentScan(value = "io.binghe.spring.annotation.chapter02", includeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
}, useDefaultFilters = false)5.掃描時(shí)按照指定的類(lèi)型進(jìn)行過(guò)濾
使用@ComponentScan注解進(jìn)行包掃描時(shí),按照給定的類(lèi)型只包含DemoService類(lèi)(接口)或其子類(lèi)(實(shí)現(xiàn)類(lèi)或子接口)的組件,如下所示。
@ComponentScan(value = "io.binghe.spring.annotation.chapter02", includeFilters = {
@Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {DemoService.class})
}, useDefaultFilters = false)此時(shí),只要是DemoService類(lèi)型的組件,都會(huì)被加載到容器中。也就是說(shuō):當(dāng)DemoService是一個(gè)Java類(lèi)時(shí),DemoService類(lèi)及其子類(lèi)都會(huì)被加載到Spring容器中;當(dāng)DemoService是一個(gè)接口時(shí),其子接口或?qū)崿F(xiàn)類(lèi)都會(huì)被加載到Spring容器中。
6.掃描時(shí)按照ASPECTJ表達(dá)式進(jìn)行過(guò)濾
使用@ComponentScan注解進(jìn)行包掃描時(shí),按照ASPECTJ表達(dá)式進(jìn)行過(guò)濾,如下所示。
@ComponentScan(value = "io.binghe.spring.annotation.chapter02", includeFilters = {
@Filter(type = FilterType.ASPECTJ, classes = {AspectJTypeFilter.class})
}, useDefaultFilters = false)其中,AspectJTypeFilter類(lèi)就是自定義的ASPECTJ表達(dá)式的過(guò)濾器類(lèi)。
7.掃描時(shí)按照正則表達(dá)式進(jìn)行過(guò)濾
使用@ComponentScan注解進(jìn)行包掃描時(shí),按照正則表達(dá)式進(jìn)行過(guò)濾,如下所示。
@ComponentScan(value = "io.binghe.spring.annotation.chapter02", includeFilters = {
@Filter(type = FilterType.REGEX, classes = {RegexPatternTypeFilter.class})
}, useDefaultFilters = false)其中,RegexPatternTypeFilter類(lèi)就是自定義的正則表達(dá)式的過(guò)濾器類(lèi)。
8.掃描時(shí)按照自定義規(guī)則進(jìn)行過(guò)濾
如果實(shí)現(xiàn)自定義規(guī)則進(jìn)行過(guò)濾時(shí),自定義規(guī)則的類(lèi)必須是org.springframework.core.type.filter.TypeFilter接口的實(shí)現(xiàn)類(lèi)。
例如,按照自定義規(guī)則進(jìn)行過(guò)濾,首先,需要?jiǎng)?chuàng)建一個(gè)org.springframework.core.type.filter.TypeFilter接口的實(shí)現(xiàn)類(lèi)BingheTypeFilter,如下所示。
public class BingheTypeFilter implements TypeFilter {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
return false;
}
}當(dāng)實(shí)現(xiàn)TypeFilter接口時(shí),需要實(shí)現(xiàn)TypeFilter接口中的match()方法,match()方法的返回值為boolean類(lèi)型。當(dāng)返回true時(shí),表示符合過(guò)濾規(guī)則,會(huì)將類(lèi)的Bean定義信息注冊(cè)到IOC容器中;當(dāng)返回false時(shí),表示不符合過(guò)濾規(guī)則,對(duì)應(yīng)的類(lèi)的Bean定義信息不會(huì)注冊(cè)到IOC容器中。
接下來(lái),使用@ComponentScan注解進(jìn)行如下配置。
@ComponentScan(value = "io.binghe.spring.annotation.chapter02", includeFilters = {
@Filter(type = FilterType.CUSTOM, classes = {BingheTypeFilter.class})
}, useDefaultFilters = false)
四、源碼時(shí)序圖?
結(jié)合時(shí)序圖理解源碼會(huì)事半功倍,你覺(jué)得呢?
本節(jié),就以源碼時(shí)序圖的方式,直觀的感受下@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í)行流程,會(huì)涉及到ComponentScanTest類(lèi)、AnnotationConfigApplicationContext類(lèi)、AbstractApplicationContext類(lèi)、PostProcessorRegistrationDelegate類(lèi)、ConfigurationClassPostProcessor類(lèi)、ConfigurationClassParser類(lèi)、ComponentScanAnnotationParser類(lèi)、ClassPathBeanDefinitionScanner類(lèi)、ClassPathScanningCandidateComponentProvider類(lèi)、BeanDefinitionReaderUtils類(lèi)和DefaultListableBeanFactory類(lèi)等。具體的源碼執(zhí)行細(xì)節(jié)參見(jiàn)源碼解析部分。
五、源碼解析?
源碼時(shí)序圖整清楚了,那就整源碼解析唄!
@ComponentScans注解與@ComponentScan注解在Spring源碼中的執(zhí)行流程,結(jié)合源碼執(zhí)行的時(shí)序圖,會(huì)理解的更加深刻。
(1)運(yùn)行案例程序啟動(dòng)類(lèi)
案例程序啟動(dòng)類(lèi)源碼詳見(jiàn):spring-annotation-chapter-02工程下的io.binghe.spring.annotation.chapter02.componentscan.ComponentScanTest,運(yùn)行ComponentScanTest類(lèi)的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類(lèi)的main()方法中調(diào)用了AnnotationConfigApplicationContext類(lèi)的構(gòu)造方法,并傳入了ComponentScanConfig類(lèi)的Class對(duì)象來(lái)創(chuàng)建IOC容器。接下來(lái),會(huì)進(jìn)入AnnotationConfigApplicationContext類(lèi)的構(gòu)造方法。
(2)解析AnnotationConfigApplicationContext類(lèi)的AnnotationConfigApplicationContext(Class>... componentClasses)構(gòu)造方法
源碼詳見(jiàn):org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(Class>... componentClasses)。
public AnnotationConfigApplicationContext(Class>... componentClasses) {
this();
register(componentClasses);
refresh();
}可以看到,在上述構(gòu)造方法中,調(diào)用了refresh()方法來(lái)刷新IOC容器。
(3)解析AbstractApplicationContext類(lèi)的refresh()方法
源碼詳見(jiàn):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中一個(gè)非常重要的方法,很多重要的功能和特性都是通過(guò)refresh()方法進(jìn)行注入的??梢钥吹?,在refresh()方法中,調(diào)用了invokeBeanFactoryPostProcessors()方法。
(4)解析AbstractApplicationContext類(lèi)的invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory)方法
源碼詳見(jiàn):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類(lèi)的invokeBeanFactoryPostProcessors()方法中調(diào)用了PostProcessorRegistrationDelegate類(lèi)的invokeBeanFactoryPostProcessors()方法。
(5)解析PostProcessorRegistrationDelegate類(lèi)的invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, ListbeanFactoryPostProcessors)方法
源碼詳見(jiàn):org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, ListbeanFactoryPostProcessors)。
由于方法的源碼比較長(zhǎng),這里,只關(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類(lèi)的invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, ListbeanFactoryPostProcessors)方法中,BeanDefinitionRegistryPostProcessor的實(shí)現(xiàn)類(lèi)在執(zhí)行邏輯上會(huì)有先后順序,并且最終都會(huì)調(diào)用invokeBeanDefinitionRegistryPostProcessors()方法。
(6)解析PostProcessorRegistrationDelegate類(lèi)的invokeBeanDefinitionRegistryPostProcessors(Collection extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry, ApplicationStartup applicationStartup)方法
源碼詳見(jiàn):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ì)循環(huán)遍歷postProcessors集合中的每個(gè)元素,調(diào)用postProcessBeanDefinitionRegistry()方法注冊(cè)Bean的定義信息。
(7)解析ConfigurationClassPostProcessor類(lèi)的postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)方法
源碼詳見(jiàn):org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)。
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
//##########省略其他代碼###################
processConfigBeanDefinitions(registry);
}
可以看到,在postProcessBeanDefinitionRegistry()方法中,會(huì)調(diào)用processConfigBeanDefinitions()方法。
(8)解析ConfigurationClassPostProcessor類(lèi)的processConfigBeanDefinitions(BeanDefinitionRegistry registry)方法
源碼詳見(jiàn):org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions(BeanDefinitionRegistry registry)。
這里,重點(diǎn)關(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)建了一個(gè)ConfigurationClassParser類(lèi)型的對(duì)象parser,并且調(diào)用了parser的parse()方法來(lái)解析類(lèi)的配置信息。
(9)解析ConfigurationClassParser類(lèi)的parse(SetconfigCandidates)方法
源碼詳見(jiàn):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;
}
名稱(chēng)欄目:一篇了解@ComponentScan注解
網(wǎng)站鏈接:http://m.5511xx.com/article/ccsppji.html


咨詢
建站咨詢
