工程搭建
Maven依賴:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.5</version>
</dependency>
</dependencies>
在項目下建立一個
byx.test
包,然後在裡面添加
A
、
B
、
Config
三個類:
public class A {
}
public class B {
}
@Component
public class Config {
}
A
和
B
是兩個普通的類(沒有标注
Component
)注解,
Config
标注了
Component
注解,是以理論上隻有
Config
會被注冊到容器中。
然後再添加一個
Main
類作為啟動類:
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
函數中,建立了一個
AnnotationConfigApplicationContext
,然後輸出容器中所有bean的name。
最終的項目結構是這樣的:

運作
Main
,控制台輸出如下:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
config
可以看到,容器中一共有5個bean,其中四個帶
internal
的都是Spring内部自帶的,
config
則是我們之前定義的
Config
類,下面就來探究一下這些元件是如何被注冊的。
AnnotationConfigApplicationContext元件注冊流程
在
main
函數的這一行加一個斷點,并啟動調試:
首先step into,然後多次step over,直到進入
AnnotationConfigApplicationContext
的構造函數:
繼續step over,執行完
this()
調用:
這裡分享一個調試Spring的小技巧,就是通過觀察
BeanFactory
内部的
beanDefinitionMap
這個成員變量來分析元件注冊的時機。
beanDefinitionMap
是一個
ConcurrentHashMap
,它的鍵是bean的name,值是對應的
BeanDefinition
。通過觀察這個變量,我們就可以知道目前容器中所有已注冊的bean資訊。
現在把注意力放在調試器的Varieables面闆,找到
this.beanFactory.beanDefinitionMap
這個變量。
可以看到,
beanDefinitionMap
的大小為4,裡面已經有了四個bean:
這四個bean都是Spring内部自帶的元件,由此可推測,Spring内部自帶的元件的注冊是在
this()
調用中,即
AnnotationConfigApplicationContext
的預設構造函數中完成的,。
繼續step over,執行完
scan(basePackages)
這行後,發現
beanDefinitionMap
的大小變成了5,增加了一個name為config的bean,正是我們自定義的
Config
類(該類被
Component
注解标注):
由此可推測,被
Component
注解标注的類是在
scan(basePackages)
調用中被注冊的。從方法名可以推測,其内部執行了一個包掃描的操作。
Spring内置元件的注冊
回到
AnnotationConfigApplicationContext
的構造函數:
public AnnotationConfigApplicationContext(String... basePackages) {
this();
scan(basePackages);
refresh();
}
從上面的分析可以知道,
AnnotationConfigApplicationContext
在它的預設構造函數中注冊内部元件,即
this()
調用,實作如下:
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);
}
使用調試器跟蹤,同時注意
beanDefinitionMap
的變化,發現注冊操作發生在
this.reader = new AnnotatedBeanDefinitionReader(this)
這行代碼中,是以直接檢視
AnnotatedBeanDefinitionReader
的構造函數:
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
this(registry, getOrCreateEnvironment(registry));
}
繼續進入另一個構造函數:
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);
}
真正的注冊操作發生在
AnnotationConfigUtils
的
registerAnnotationConfigProcessors
方法中:
public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) {
registerAnnotationConfigProcessors(registry, null);
}
繼續進入
registerAnnotationConfigProcessors
重載方法,終于看到了核心代碼(省略了一部分無關緊要的内容):
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
BeanDefinitionRegistry registry, @Nullable Object source) {
...
Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);
// 注冊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));
}
// 注冊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));
}
...
// 注冊org.springframework.context.event.internalEventListenerProcessor
// 與EventListener有關
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));
}
// 注冊org.springframework.context.event.internalEventListenerFactory
// 還是與EventListener有關
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
方法内部注冊了我們在控制台輸出中看到的四個Spring内置元件。
Component注解的處理
回到
AnnotationConfigApplicationContext
的構造函數:
public AnnotationConfigApplicationContext(String... basePackages) {
this();
scan(basePackages);
refresh();
}
從上面的分析可以知道,
scan(basePackages)
這個調用負責掃描并注冊被
Component
标注的bean,該方法的實作如下:
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)
這個調用,其中
this.scanner
是一個
ClassPathBeanDefinitionScanner
的執行個體,它在
AnnotationConfigApplicationContext
的預設構造函數中被初始化:
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
方法實作如下:
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
方法内部調用了
doScan
方法,同時還記錄了bean數量的改變量。
doScan
方法實作如下:
// basePackages就是我們在main函數中構造AnnotationConfigApplicationContext時傳入的包名
// 從這裡也可以看出,我們可以同時傳入多個包名
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);
// 周遊找到的每個BeanDefinition
for (BeanDefinition candidate : candidates) {
// 設定scope屬性
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
// 生成beanName
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
// 設定一些預設屬性
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);
// 真正執行注冊操作
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
// 傳回所有BeanDefinitionHolder
return beanDefinitions;
}
doScan
方法是注冊bean的核心邏輯,它周遊每個傳入的包名,通過調用
findCandidateComponents
方法來擷取每個包下滿足條件的bean,然後進行一些必要的設定,最後調用
registerBeanDefinition
方法完成注冊操作。
findCandidateComponents
方法的實作如下:
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
}
else {
return scanCandidateComponents(basePackage);
}
}
scanCandidateComponents
方法的實作如下:
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<>();
try {
// 将包名轉換成一個資源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();
// 周遊所有資源,每個資源表示一個.class檔案
for (Resource resource : resources) {
if (traceEnabled) {
logger.trace("Scanning " + resource);
}
if (resource.isReadable()) {
try {
// 擷取class的中繼資料,包括注解的資訊
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
// 判斷是否滿足條件
if (isCandidateComponent(metadataReader)) {
// 構造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内置的資源讀取機制讀取指定包下的所有class檔案,然後轉換成
MetadataReader
,并傳入
isCandidateComponent
方法判斷是否滿足要求,如果滿足要求則加入
isCandidateComponent
集合。
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;
}
isCandidateComponent
方法通過
excludeFilters
和
includeFilters
兩個集合來對
MetadataReader
進行過濾。在調試中可以發現,
includeFilters
包含了一個
Component
注解的過濾器,是以可以過濾出标注了
Component
的類。
如果使用調試器調試程式,可以發現,
isCandidateComponent
方法隻會對
Config
類傳回
true
,而對其他類(
A
、
B
、
Main
)都傳回
false
。
includeFilters
的初始化是在
org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#registerDefaultFilters
方法中被初始化的(
ClassPathScanningCandidateComponentProvider
是
ClassPathBeanDefinitionScanner
的父類):
protected void registerDefaultFilters() {
this.includeFilters.add(new AnnotationTypeFilter(Component.class));
...
}
到此,
Component
注解的處理過程就分析完了。