spring 學習-bean建立-scan掃描bean
概述
文章分析註解方式注入bean 的程式碼過程。本篇將介紹 AnnotationConfigApplicationContext 該類的關於掃描註釋關於 bean 的過程。
前言
我們使用過spring
框架 ,知道了生成 bean 的方式可以有 XML 配置檔案, 也可以通過註解。我們分析原始碼前可以思考假如給你一個XML檔案最終要裝配到容器中去,你的步驟應該是如何的 :
- 讀取
- 解析
- 註冊
spring 中也是遵循這個思路的, 我們先看一下 BeanFactory 和 Context 的關係 ,並且瞭解一下他們的繼承關係 , 這兩個類是生成 bean 重要的兩個介面。
註釋方式注入Bean 方式
註解式生成bean的過程,總的來說就是 :
-
初始化一個Context
-
掃描生成 BeanDefinition
-
呼叫 refresh 方法
其中第一個步驟由於是註解類,利用第一步初始化生成的 Scan 類進行掃描特定包下的 bean ,然後根據屬性,例如作用域,單例模式還是模板模式等屬性生成 BeanDefinition ,最後呼叫 上下文 context 父類的refresh 方法,進行呼叫(BeanFactoryPostProcess)後置處理器處理方法和BeanPostProcess處理器處理方法。
Demo 1
public static void main(String[] args) { AnnotationConfigApplicationContext consumerContext = new AnnotationConfigApplicationContext(); //這裡看到我們註冊了一個 configuration 的類進行配置,然後重新整理一下容器得到最新的結果 consumerContext.register(ConsumerConfiguration.class); consumerContext.refresh(); //從容器中獲取類 FooServiceConsumer service = consumerContext.getBean(FooServiceConsumer.class); ... }
Demo 2
idea 中new一個新的 spring專案,然後編寫兩個類 。
@Component public class MyService { public String sayHello(){ return "hello world "; } } public class SpringTest { public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext("main");//A MyService myService = ctx.getBean(MyService.class); String s = myService.sayHello(); System.out.println("s : "+ s); } }
我們以例子二為原始碼閱讀,從 A 處 debug 進去,看一下 bean 的載入和建立
AnnotationConfigApplicationContext 的建立過程概述
構造方法很清晰 : 初始化,掃描,重新整理三個步驟。
public AnnotationConfigApplicationContext(String... basePackages) {
//初始化
this();
//掃描包
scan(basePackages);
//重新整理
refresh();
}
原始碼分析
需要知道的類 :
Resoure
對應讀取操作BeanDefinitionRead
對應解析操作Registry
對應註冊操作。
以AnnotationConfigApplicationContext
為例子,檢視類圖主要包含三個型別的介面
AppContext介面
: 上下文相關Lifecycle介面
: 生命週期相關Registry介面
: 用於註冊 BeanDefinition
AnnotationConfigApplicationContext 的初始化建立的兩個類作用如下 :
AnnotatedBeanDefinitionReader
:註冊幾個後置處理器ClassPathBeanDefinitionScanner
:掃描包內的bean
public AnnotationConfigApplicationContext(String... basePackages) {
//初始化
this();
//掃描包
scan(basePackages);
//重新整理
refresh();
}
/**
* Create a new AnnotationConfigApplicationContext that needs to be populated
* through {@link #register} calls and then manually {@linkplain #refresh refreshed}.
*
* 構造方法,從註釋也可以看到,構造完,需要呼叫 register 方法進行填充和 refresh 方法進行重新整理
*/
public AnnotationConfigApplicationContext() {
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
//============ 父類初始化 ==============
//==========================
//GenericApplicationContext
public GenericApplicationContext() {
this.beanFactory = new DefaultListableBeanFactory();
}
//AbstractApplicationContext
public AbstractApplicationContext() {
this.resourcePatternResolver = getResourcePatternResolver();
}
//DefaultResourceLoader
public DefaultResourceLoader() {
this.classLoader = ClassUtils.getDefaultClassLoader();
}
public static ClassLoader getDefaultClassLoader() {
ClassLoader cl = null;
try {
cl = Thread.currentThread().getContextClassLoader();
}
catch (Throwable ex) {
// Cannot access thread context ClassLoader - falling back...
}
if (cl == null) {
// No thread context class loader -> use class loader of this class.
cl = ClassUtils.class.getClassLoader();
if (cl == null) {
// getClassLoader() returning null indicates the bootstrap ClassLoader
try {
cl = ClassLoader.getSystemClassLoader();
}
catch (Throwable ex) {
// Cannot access system ClassLoader - oh well, maybe the caller can live with null...
}
}
}
return cl;
}
//============ 父類初始化 ==============
//============ AnnotatedBeanDefinitionReader 類初始化 ==============
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;
//步驟一 : 用於解析 @Condition 註解相關
this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
//步驟二 : 最終會呼叫 register®isterBeanDefinition方法註冊入幾個預設的 BeanDefinition
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
其中步驟二中注入的類包括 :
作用:內部託管配置註釋處理器。
對應的類:ConfigurationClassPostProcessor.class
作用:內部管理的自動注入註解處理器
對應的類:AutowiredAnnotationBeanPostProcessor.class
作用:內部管理的JSR-250註釋處理器
對應的類:CommonAnnotationBeanPostProcessor.class
作用:內部管理的JPA註釋處理器(不一定注入)。
對應的類:PersistenceAnnotationBeanPostProcessor.class
作用:內部管理的@EventListener註釋處理器
對應的類:EventListenerMethodProcessor.class
作用:內部管理的EventListenerFactory。
對應的類:DefaultEventListenerFactory.class
BeanDefinition
是個介面,我們看一下 ClassPathBeanDefinitionScanner 會掃描我們專案中的 bean
該類註解 :
A bean definition scanner that detects bean candidates on the classpath, registering corresponding bean definitions with a given registry (BeanFactory or ApplicationContext).
Candidate classes are detected through configurable type filters. The default filters include classes that are annotated with Spring's @Component, @Repository, @Service, or @Controller stereotype.
下面我們看一下核心的scan 方法,它的呼叫棧挺長的,我們只需要知道scan 中會進行 :
- 掃描所有 bean
- 新增幾個postprocess(後置處理器)作為 beanDefinition ,方便後續的
@Override
public void scan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
//使用上面構造方法生成的 scan 物件,執行 scan 方法
this.scanner.scan(basePackages);
}
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);
}
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) {
//這裡會把配置檔案中的 bean 讀取出來,然後進行註冊
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
//為每個 BeanDefinition 填充作為 BeanDefinition 的屬性值
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
//檢查這個 BeanDefinition 是否一件存在,因為預設是單例,可以想象得到肯定是去 DefaultListableBeanFactory 中判斷尋找
if (checkCandidate(beanName, candidate)) {
//封裝成 BeanDefinitionHolder 物件
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
// register 註冊
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) {
//這裡呼叫的一個工具類的靜態方法
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
}
//BeanDefinitionReaderUtils 類方法
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// Register bean definition under primary name.
String beanName = definitionHolder.getBeanName();
//最終呼叫的是 registry 的 registerBeanDefinition 方法
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// Register aliases for bean name, if any.
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
繼續看 , 也就是說我們掃描到的類跑到了 registry ,而這個 registry 是在哪裡傳進來的呢?
public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {
private final AnnotatedBeanDefinitionReader reader;
private final ClassPathBeanDefinitionScanner scanner;
/**
* Create a new AnnotationConfigApplicationContext that needs to be populated
* through {@link #register} calls and then manually {@linkplain #refresh refreshed}.
*/
public AnnotationConfigApplicationContext() {
StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create");
this.reader = new AnnotatedBeanDefinitionReader(this);
createAnnotatedBeanDefReader.end();
// context 把自己傳給了我們上面的掃描物件
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
...
}
//看一下我們剛才上面分析的掃描物件
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
Environment environment, @Nullable ResourceLoader resourceLoader) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
this.registry = registry;
if (useDefaultFilters) {
registerDefaultFilters();
}
setEnvironment(environment);
setResourceLoader(resourceLoader);
}
...
}
ok, scan 方法我們就瞭解這麼多,下篇我們看一下 refresh方法。
總結
該篇文章我們介紹了 AnnotationConfigApplicationContext 的初始化過程,剩下的 refresh 方法將會留到下一篇文章進行講解。
參考資料
- https://blog.csdn.net/csdn_20150804 (spring5 系列分析)
- https://juejin.im/post/5d7afbc6518825345a05c549 (小分析)