工程搭建
Maven依賴:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.5</version>
</dependency>
</dependencies>
在項(xiàng)目下新建一個(gè)byx.test
包,然后在里面添加A
踢械、B
、Config
三個(gè)類:
public class A {
}
public class B {
}
@Component
public class Config {
}
A
和B
是兩個(gè)普通的類(沒有標(biāo)注Component
)注解,Config
標(biāo)注了Component
注解坐搔,所以理論上只有Config
會(huì)被注冊(cè)到容器中。
然后再添加一個(gè)Main
類作為啟動(dòng)類:
public class Main {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext("byx.test");
// 輸出容器中的所有bean的name
for (String name : ctx.getBeanDefinitionNames()) {
System.out.println(name);
}
}
}
在main
函數(shù)中敬矩,創(chuàng)建了一個(gè)AnnotationConfigApplicationContext
概行,然后輸出容器中所有bean的name。
最終的項(xiàng)目結(jié)構(gòu)是這樣的:
運(yùn)行Main
弧岳,控制臺(tái)輸出如下:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
config
可以看到凳忙,容器中一共有5個(gè)bean业踏,其中四個(gè)帶internal
的都是Spring內(nèi)部自帶的,config
則是我們之前定義的Config
類涧卵,下面就來探究一下這些組件是如何被注冊(cè)的堡称。
AnnotationConfigApplicationContext組件注冊(cè)流程
在main
函數(shù)的這一行加一個(gè)斷點(diǎn),并啟動(dòng)調(diào)試:
首先step into艺演,然后多次step over却紧,直到進(jìn)入AnnotationConfigApplicationContext
的構(gòu)造函數(shù):
繼續(xù)step over,執(zhí)行完this()
調(diào)用:
這里分享一個(gè)調(diào)試Spring的小技巧胎撤,就是通過觀察BeanFactory
內(nèi)部的beanDefinitionMap
這個(gè)成員變量來分析組件注冊(cè)的時(shí)機(jī)晓殊。beanDefinitionMap
是一個(gè)ConcurrentHashMap
,它的鍵是bean的name,值是對(duì)應(yīng)的BeanDefinition
么鹤。通過觀察這個(gè)變量筑辨,我們就可以知道當(dāng)前容器中所有已注冊(cè)的bean信息。
現(xiàn)在把注意力放在調(diào)試器的Varieables面板介汹,找到this.beanFactory.beanDefinitionMap
這個(gè)變量。
可以看到舶沛,beanDefinitionMap
的大小為4嘹承,里面已經(jīng)有了四個(gè)bean:
這四個(gè)bean都是Spring內(nèi)部自帶的組件,由此可推測(cè)如庭,Spring內(nèi)部自帶的組件的注冊(cè)是在this()
調(diào)用中叹卷,即AnnotationConfigApplicationContext
的默認(rèn)構(gòu)造函數(shù)中完成的,坪它。
繼續(xù)step over骤竹,執(zhí)行完scan(basePackages)
這行后,發(fā)現(xiàn)beanDefinitionMap
的大小變成了5往毡,增加了一個(gè)name為config的bean蒙揣,正是我們自定義的Config
類(該類被Component
注解標(biāo)注):
由此可推測(cè),被Component
注解標(biāo)注的類是在scan(basePackages)
調(diào)用中被注冊(cè)的开瞭。從方法名可以推測(cè)懒震,其內(nèi)部執(zhí)行了一個(gè)包掃描的操作。
Spring內(nèi)置組件的注冊(cè)
回到AnnotationConfigApplicationContext
的構(gòu)造函數(shù):
public AnnotationConfigApplicationContext(String... basePackages) {
this();
scan(basePackages);
refresh();
}
從上面的分析可以知道惩阶,AnnotationConfigApplicationContext
在它的默認(rèn)構(gòu)造函數(shù)中注冊(cè)內(nèi)部組件挎狸,即this()
調(diào)用,實(shí)現(xiàn)如下:
public AnnotationConfigApplicationContext() {
StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create");
this.reader = new AnnotatedBeanDefinitionReader(this);
createAnnotatedBeanDefReader.end();
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
使用調(diào)試器跟蹤断楷,同時(shí)注意beanDefinitionMap
的變化锨匆,發(fā)現(xiàn)注冊(cè)操作發(fā)生在this.reader = new AnnotatedBeanDefinitionReader(this)
這行代碼中,所以直接查看AnnotatedBeanDefinitionReader
的構(gòu)造函數(shù):
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
this(registry, getOrCreateEnvironment(registry));
}
繼續(xù)進(jìn)入另一個(gè)構(gòu)造函數(shù):
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
Assert.notNull(environment, "Environment must not be null");
this.registry = registry;
this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
真正的注冊(cè)操作發(fā)生在AnnotationConfigUtils
的registerAnnotationConfigProcessors
方法中:
public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) {
registerAnnotationConfigProcessors(registry, null);
}
繼續(xù)進(jìn)入registerAnnotationConfigProcessors
重載方法,終于看到了核心代碼(省略了一部分無關(guān)緊要的內(nèi)容):
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
BeanDefinitionRegistry registry, @Nullable Object source) {
...
Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);
// 注冊(cè)org.springframework.context.annotation.internalConfigurationAnnotationProcessor
// ConfigurationClassPostProcessor用來處理Configuration注解
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// 注冊(cè)org.springframework.context.annotation.internalAutowiredAnnotationProcessor
// AutowiredAnnotationBeanPostProcessor用來處理Autowired注解
if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
}
...
// 注冊(cè)org.springframework.context.event.internalEventListenerProcessor
// 與EventListener有關(guān)
if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
}
// 注冊(cè)org.springframework.context.event.internalEventListenerFactory
// 還是與EventListener有關(guān)
if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
}
return beanDefs;
}
registerAnnotationConfigProcessors
方法內(nèi)部注冊(cè)了我們?cè)诳刂婆_(tái)輸出中看到的四個(gè)Spring內(nèi)置組件恐锣。
Component注解的處理
回到AnnotationConfigApplicationContext
的構(gòu)造函數(shù):
public AnnotationConfigApplicationContext(String... basePackages) {
this();
scan(basePackages);
refresh();
}
從上面的分析可以知道茅主,scan(basePackages)
這個(gè)調(diào)用負(fù)責(zé)掃描并注冊(cè)被Component
標(biāo)注的bean,該方法的實(shí)現(xiàn)如下:
public void scan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
StartupStep scanPackages = this.getApplicationStartup().start("spring.context.base-packages.scan")
.tag("packages", () -> Arrays.toString(basePackages));
this.scanner.scan(basePackages);
scanPackages.end();
}
真正干活的是this.scanner.scan(basePackages)
這個(gè)調(diào)用土榴,其中this.scanner
是一個(gè)ClassPathBeanDefinitionScanner
的實(shí)例诀姚,它在AnnotationConfigApplicationContext
的默認(rèn)構(gòu)造函數(shù)中被初始化:
public AnnotationConfigApplicationContext() {
StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create");
this.reader = new AnnotatedBeanDefinitionReader(this);
createAnnotatedBeanDefReader.end();
this.scanner = new ClassPathBeanDefinitionScanner(this); // 初始化scanner
}
ClassPathBeanDefinitionScanner
的scan
方法實(shí)現(xiàn)如下:
public int scan(String... basePackages) {
int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
doScan(basePackages);
// Register annotation config processors, if necessary.
if (this.includeAnnotationConfig) {
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}
scan
方法內(nèi)部調(diào)用了doScan
方法,同時(shí)還記錄了bean數(shù)量的改變量玷禽。doScan
方法實(shí)現(xiàn)如下:
// basePackages就是我們?cè)趍ain函數(shù)中構(gòu)造AnnotationConfigApplicationContext時(shí)傳入的包名
// 從這里也可以看出赫段,我們可以同時(shí)傳入多個(gè)包名
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
// 遍歷每個(gè)包名
for (String basePackage : basePackages) {
// 尋找每個(gè)包下符合條件的類,并包裝成BeanDefinition
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
// 遍歷找到的每個(gè)BeanDefinition
for (BeanDefinition candidate : candidates) {
// 設(shè)置scope屬性
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
// 生成beanName
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
// 設(shè)置一些默認(rèn)屬性
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
// 處理Lazy矢赁、Primary糯笙、DependsOn、Role撩银、Description這些注解
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
// 把BeanDefinition包裝成BeanDefinitionHolder
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
// 真正執(zhí)行注冊(cè)操作
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
// 返回所有BeanDefinitionHolder
return beanDefinitions;
}
doScan
方法是注冊(cè)bean的核心邏輯给涕,它遍歷每個(gè)傳入的包名,通過調(diào)用findCandidateComponents
方法來獲取每個(gè)包下滿足條件的bean额获,然后進(jìn)行一些必要的設(shè)置够庙,最后調(diào)用registerBeanDefinition
方法完成注冊(cè)操作。
findCandidateComponents
方法的實(shí)現(xiàn)如下:
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
}
else {
return scanCandidateComponents(basePackage);
}
}
scanCandidateComponents
方法的實(shí)現(xiàn)如下:
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<>();
try {
// 將包名轉(zhuǎn)換成一個(gè)資源url
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
// 讀取資源
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
boolean traceEnabled = logger.isTraceEnabled();
boolean debugEnabled = logger.isDebugEnabled();
// 遍歷所有資源抄邀,每個(gè)資源表示一個(gè).class文件
for (Resource resource : resources) {
if (traceEnabled) {
logger.trace("Scanning " + resource);
}
if (resource.isReadable()) {
try {
// 獲取class的元數(shù)據(jù)耘眨,包括注解的信息
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
// 判斷是否滿足條件
if (isCandidateComponent(metadataReader)) {
// 構(gòu)造BeanDefinition
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
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);
}
// 返回所有滿足條件的BeanDefinition
return candidates;
}
scanCandidateComponents
方法使用Spring內(nèi)置的資源讀取機(jī)制讀取指定包下的所有class文件,然后轉(zhuǎn)換成MetadataReader
撤摸,并傳入isCandidateComponent
方法判斷是否滿足要求毅桃,如果滿足要求則加入isCandidateComponent
集合。
isCandidateComponent
方法的實(shí)現(xiàn)如下:
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;
}
isCandidateComponent
方法通過excludeFilters
和includeFilters
兩個(gè)集合來對(duì)MetadataReader
進(jìn)行過濾准夷。在調(diào)試中可以發(fā)現(xiàn),includeFilters
包含了一個(gè)Component
注解的過濾器莺掠,所以可以過濾出標(biāo)注了Component
的類衫嵌。
如果使用調(diào)試器調(diào)試程序,可以發(fā)現(xiàn)彻秆,isCandidateComponent
方法只會(huì)對(duì)Config
類返回true
楔绞,而對(duì)其他類(A
、B
唇兑、Main
)都返回false
酒朵。
includeFilters
的初始化是在org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#registerDefaultFilters
方法中被初始化的(ClassPathScanningCandidateComponentProvider
是ClassPathBeanDefinitionScanner
的父類):
protected void registerDefaultFilters() {
this.includeFilters.add(new AnnotationTypeFilter(Component.class));
...
}
到此,Component
注解的處理過程就分析完了扎附。