Spring 原始碼閱讀 二
程式入口:
接著上一篇部落格中看完了在AnnotationConfigApplicationContext
的建構函式中的register(annotatedClasses);
將我們傳遞進來的主配置類新增進了BeanFactory
, 本片部落格繼續跟進refresh();
看看Spring如何繼續初始化Spring的環境
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) { this(); register(annotatedClasses) refresh(); }
跟進refresh()
, 原始碼如下: 主要做了如下幾件工作
- 重新整理的預準備
- 比如: 設定時間的錨點,載入上下文環境變數
- 獲取BeanFactory
- 執行所有的
BeanFactoryPostProcessor
- 執行所有的
BeanPostProcessor
- ...
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. //準備重新整理 prepareRefresh(); //獲取BeanFactory ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 準備BeanFactory prepareBeanFactory(beanFactory); try { // 方法中沒有任何實現的邏輯 postProcessBeanFactory(beanFactory); // invoke BeanFactoryPostprocessor, 執行bean工廠的後置處理器 //如果我們沒有手動往Spring中注入bean工廠的後置處理器,那麼此時僅有一個,也是beanFactoryMap中的第一個RootBeanDefinition-> ConfigurationClassPostProcessor invokeBeanFactoryPostProcessors(beanFactory); // 註冊 bean的後置處理器, 這些處理器可以在bean的構造方法執行之後再執行init()方法前後執行指定的邏輯 registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } } }
重新整理的準備工作
這個方法沒啥可看的重要邏輯,記錄了下開始的時間,然後為Spring的上下文載入可用的環境變數
protected void prepareRefresh() { this.startupDate = System.currentTimeMillis(); this.closed.set(false); this.active.set(true); if (logger.isInfoEnabled()) { logger.info("Refreshing " + this); } // 這個是protected型別的方法,目前還沒有任何實現 initPropertySources(); // 校驗所有需要的properties是否都被解析過了 // getEnvironment() 得到系統環境, 後續的@Profile使用 getEnvironment().validateRequiredProperties(); this.earlyApplicationEvents = new LinkedHashSet<>(); }
獲取BeanFactory
獲取出BeanFactory
,接下來的工作重點是去掃描出程式設計師提供的類,然後將它們放進BeanFactoryMap
中,在此過程中穿插執行BeanFactoryPostProcessor
和BeanPostPorcessor
, 不難看出後續工作的進展都離不開這個BeanFactoryMap
,這個map在哪裡呢? 就在我們的beanFactory
中,因此在重新整理的最開始,獲取出bean工廠
當前類是AbstractApplicationContext
,上圖是它的繼承類圖,通過上圖可以看到,它是入口AnnotationConfigApplicationContext
和GenericApplicationContext
的父類,而Spring的BeanFactory
是在GenericApplicationContext
中例項化的,故, 獲取beanFactory
的邏輯肯定在當前方法中被設計成抽象的方法,而由自己具體實現,原始碼如下:
@Override
public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
準備beanFactory
上面的邏輯是獲取出BeanFactory
, 那什麼是準備BeanFactory
呢? 看它的註解解釋是: 為BeanFactory
配置上它應該具有的所有特徵, 那BeanFactory應該有什麼特徵呢? 類載入器 , bean表示式的解析器 , property與物件的轉換器 , bean的後置處理器 , 新增禁止使用者注入的bean的資訊 , 注入bean的替換 , 新增預設的和環境相關的bean
原始碼如下:它的解析我們寫在下面
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// 新增類載入器
beanFactory.setBeanClassLoader(getClassLoader());
// 設定bean標籤的解析器, 一般我們使用spel標籤比較多,但是Spring也有自己的Bean標籤
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
// </property ref="XXX"> 解析轉換xxx 替換成物件
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
// 新增bean的後置處理器,很顯然這裡新增的是Spring自己的後置處理器
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
// 當用戶企圖注入下面型別的物件時, 會被Spring忽略
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
//使用者穿進來的是 BeanFactory.class , 那Spring會將她替換成beanFactory
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
// Register early post-processor for detecting inner beans as ApplicationListeners.
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
// Detect a LoadTimeWeaver and prepare for weaving, if found.
if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
// Set a temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
// 意思是如果自定義的Bean中沒有名為"systemProperties"和"systemEnvironment"的Bean,
// 則註冊兩個Bena,Key為"systemProperties"和"systemEnvironment",Value為Map,
// 這兩個Bean就是一些系統配置和系統環境資訊
// 註冊預設的和環境相關的 bean Sping會檢測,我們自己註冊進來的Bean中有沒有下面的名字叫下面三個串的物件, 沒有的話就幫我們注入進來
if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) { // environment_bean_name
beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
}
if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {// system_properties_bean_name
beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
}
if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {// system_environment_bean_name
beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
}
}
如上程式碼的作用就是為BeanFactory
初始化了Spring規定的幾個必須的配置屬性,其中比較值得注意的地方就是它新增的BeanPostProcessor
, 雖然這是Spring原生的bean的後置處理器,但是也是第一次出現,很有意義,配置expressContext等工作, 這個後置處理器會在Bean的構造的構造過程中,動態的攔截插手
此外,添加了忽略注入的物件,當程式設計師向注入Spring啟動時,依賴的原生物件時,會被忽略注入,企圖注入BeanFactory,資源解析器,事件釋出器,應用上下文時,被Spring使用原生的物件替換掉
執行所有的BeanFactory的後置處理器
執行BeanFactory的後置處理器,具體是哪些呢? 其實是兩部分,一部分是使用者自己新增的,另一部分是Spring在啟動過程中自己新增進去的
先說使用者自己新增的情況,不知道大家有沒有發現,原始碼讀到這裡其實還沒看到Spring進行包掃描,既然沒有進行包掃描那程式設計師通過@Compoennt
註解新增進去的 bean工廠的後置處理器 就還沒有被Spring所識別到,沒錯,這裡能被識別到的 BeanFactoryPostProcessor
是程式設計師通過context.addBeanFactoryPostProcessor(new MyBeanFactoryPostProcessor());
新增進來的
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
}
繼續跟進invokeBeanFactoryPostProcessors()
方法,原始碼如下,這個方法在執行BeanFactoryPostProcessor
不得不說他真的很重要,很長
它的主要邏輯是: 開始是一個if
分支判斷beanFactory
的合法性,上篇博文中我們看到了,所謂的beanFactory其實本身也實現了註冊器介面,有註冊bean的功能, 於是他將BeanFactory
強轉成了註冊器型別
緊接著Spring定義了兩個新的List, 一個叫regularPostProcessors
一個叫registryProcessors
, 前者用來存放程式設計師自己新增進來的BeanFactoryPostProcessor
, 後者用來存放程式設計師自己新增進來的BeanDefinitionRegistryPostProcessor
, 為什麼使用兩個集合呢? 參見下圖:
通過上面的圖可以看到,BeanFactoryPostProcessor
是頂級的介面,BeanDefinitionRegistryPostProcessor
是繼承了頂級介面然後自己做出了拓展, 一般程式設計師通過BeanFactoryPostProcessor
在bean的構造方法之前進行插手的話,最常用的就是選擇直接自己實現BeanFactoryPostProcessor
,雖然實現BeanDefinitionRegistryPostProcessor
也行
BeanDefinitionRegistryPostProcessor
對BeanFactoryPostProcessor
做出來拓展, Spring需要保證拓展的方法被執行到,重寫的父類的方法也要被執行到,因此選擇使用兩個集合,迴圈所有的bean工廠的後置處理器,按照不同的分類分別對待
分好類之後,又建立了一個list叫currentRegistryProcessors
這個List中存放的是 Spring自己的提供的BeanFactoryPostProcessor
的實現, 其實這個實現在前面提到過好多次了. 他就是ConfigurationClassPostProcessor
現在一共是三個集合,其中兩個集合中的存放的物件是一樣的,於是Spring將其實兩個存放BeanDefinitionRegistryPostProcessor
的集合進行了合併
接下來就是真正的開始執行的工作
- 執行
BeanDefinitionRegistryPostProcessor
- 執行
BeanFactoryPostProcessor
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
// Invoke BeanDefinitionRegistryPostProcessors first, if any.
Set<String> processedBeans = new HashSet<>();
// 通過看BanFactory的繼承體系,能看到它實現了 BeanDefinitionRegistry介面
if (beanFactory instanceof BeanDefinitionRegistry) {
// 將工廠強轉成 註冊器
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
// 存放程式設計師新增進來的 BeanFactoryPostProcessor
List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
// 存放程式設計師新增進來的 BeanDefinitionRegistryPostProcessor
List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
System.out.println(beanFactoryPostProcessors.size());
// 自定義的beanFactoryPostProcessors
for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
BeanDefinitionRegistryPostProcessor registryProcessor =
(BeanDefinitionRegistryPostProcessor) postProcessor;
registryProcessor.postProcessBeanDefinitionRegistry(registry);
registryProcessors.add(registryProcessor);
}
else {//BeanDefinitionRegistryPostProcessor BeanfactoryPostProcessor
// 將自定義的BeanFactoryPostPorcessor 新增到了 上面的 ArrayList中regularPostProcessors
regularPostProcessors.add(postProcessor);
}
}
List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
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);
currentRegistryProcessors.clear();
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);
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);
currentRegistryProcessors.clear();
}
invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
...
}
接下來與其說 看一下它如何執行BeanDefinitionRegistryPostProcessor
, 到不如說 看一下如何執行ConfigrationClassPostProcessor
對BeanFactoryPostProcessor
的拓展方法
postProcessor.postProcessBeanDefinitionRegistry(registry);
繼續跟進,進過兩個沒有啥重要邏輯的方法之後,進入到下面的邏輯中, 有來了一個高潮,在這個方法中完成了包掃描工作
一開始獲取出beanFactoryMap
中的全部的BeanDefinitionName
, 這時一共有幾個? 其實是7個, 其中6個是在建立AnnotatedBeanDefinitionReader
時新增進去的6個, 另外的哪一個就是在前面的register()
方法中,註冊的我們的主配置類MainConfig
緊接著是一個迴圈判斷語句,迴圈這七個BeanDefinition
, 目的有兩個,第一個把我們自己的MainConfig
配置類找出來放到下面的configCandidates
集合中,因為這是個配置類啊,上面會有@ComponentScan(value="XXX")
通過這個註解提供的包資訊,Spring就能進一步進行包掃描,找到使用者提供的所有的類資訊,將它們載入進容器中, 第二個判斷一下我們的MainConfig
類上有沒有新增@Configuration
如果存在這個註解標記它為full,Spring就認為我們的當前的執行的上下文環境是全註解環境,並且會為MainConfig
生成一個cglib代理物件,進一步保證了Spring的單例特徵,如果沒有這個註解,但是存在@Component @ComponentScan @Import @ImportResource
Spring標記它為lite.認為當前的上下文環境為非全註解模式
怎麼理解這個全註解與非全註解呢? 字面意思也是,全註解就是不存在配置檔案, 不存在配置檔案的話,程式設計師不可能不提供@ComponentScan
讓Spring去掃描完成Bean的注入,同時程式設計師也會提供一個@Configuration
明確的標識這是一個配置類, 那非全註解呢, 就是可能存在註解和XML共存的現象, Spring這時也會同時支援註解+xml的讀取
接著又是排序,建立名稱生成器
緊接著建立了一個ConfigurationClassParser
配置類的解析器,這個解析器,見名知意,用來解析配置類, 現在誰是配置類呢? 其實就是在上面的迴圈中唯一被新增進list的,我們提供的MainConfig
, 因為我在他身上添加了@Configuration
註解
下面的主要邏輯是解析配置類,我把解釋寫在如下程式碼的下面
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
// 定義一個List 存放專案中添加了 @Compennt註解的類
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
// 獲取容器中註冊的所有bd名字
// 一共 7個 , 6個rootBeanDefinition 1個我們自己的MainConfig
// 獲取出一開始我們Spring自己新增的6個Processor, 和我們新增的MainConfig
String[] candidateNames = registry.getBeanDefinitionNames();
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
//
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// 他是怎麼判斷出來的呢? 在上面, 如果判斷得出當前的類添加了@Configration , 就給他標記 full, 在上面的if分支語句中,添加了full的類,不會新增進 configCandidates 中,故為空
if (configCandidates.isEmpty()) {
return;
}
// Sort by previously determined @Order value, if applicable
configCandidates.sort((bd1, bd2) -> {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return Integer.compare(i1, i2);
});
// Detect any custom bean name generation strategy supplied through the enclosing application context
SingletonBeanRegistry sbr = null;
// 現在我們使用的BeanDefinitionRegistry是其實是Spring的Bean工廠(DefaultListableBeanFactory) 他是SingletonBeanRegistry的子類的話
if (registry instanceof SingletonBeanRegistry) {
// 將registry強轉為SingletonBeanRegistry
sbr = (SingletonBeanRegistry) registry;
if (!this.localBeanNameGeneratorSet) {
// 是否有自定義的
BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
//SingletonBeanRegistry中有id為 org.springframework.context.annotation.internalConfigurationBeanNameGenerator
// 如果有則利用他的,否則是spring預設的
if (generator != null) {
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
}
if (this.environment == null) {
this.environment = new StandardEnvironment();
}
// 這是個配置類的解析器 會解析每一個添加了 @Configuration 的類
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
// 僅僅處理添加了@Configuration註解的類, 進行包掃描,跟進去
parser.parse(candidates);
// 執行到這裡完成了掃描,BeanFactory中的BeanDefinitionMap中就多了我們位元組新增進去的bean資訊
parser.validate();
//map.keyset
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
candidates.clear();
...
跟進這個parser.parse(candidates);
,這裡就來到了又一波高潮,準備開始包掃描了
不怕麻煩,再提一下,當前的這個物件就是我們的MianConfig
,它是被AnnotatedBeanDefinitionReader
讀取到的,所以它一定是AnnotatedBeanDefinition
, 所以一定會進入到第一個if分支中
public void parse(Set<BeanDefinitionHolder> configCandidates) {
this.deferredImportSelectors = new LinkedList<>();
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());
}
繼續跟進,同樣是經過了幾個沒有重要邏輯的方法之後,進入到下面的方法中,看他是如何下面這個重要的方法中
這個方法主要做了兩件大事:
- 處理掃描新增有
@Component
註解的普通類,並將它們直接新增到BeanFactoryMap
中 - 掃描處理新增有
@Import
註解
看他首先取出所有的@CompoenntScan
註解,迴圈遍歷註解,每次迴圈都使用this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
進行真正的包掃描
跟進這個方法中,可以看到它一開始就自己重新new 了一個包掃描器,然後解析當前迴圈的@ComponentScan
註解上的其他如excludeFilters,includeFilters
等屬性,最後開始真正的進行包掃描, 在這個掃描的過程中,會將命中符合條件的普通類(如被@Component
標識),進行如下處理
- 設定scope資訊
- 生成BeanName
- 給掃描出來的這些新增上預設的屬性資訊比如預設全是
Lazy
- 進一步,處理這些類上的註解資訊,比如
@Lazy , @Primary , @DependsOn , @Role , @Description
,用這些資訊覆蓋預設的資訊 - 將掃描出來的普通類直接新增到BeanFactoryMap中
完成了上面的普通類的掃描工作之後,下一個高潮就來了,處理@Import()的三中情況,它的解析我寫在如下程式碼的下面
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
// Recursively process any member (nested) classes first
// 遞迴地首先處理任何成員(巢狀)類
processMemberClasses(configClass, sourceClass);
// Process any @PropertySource annotations
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
}
else {
logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
// 處理所有的@ComponentScan註解, 也就是讀取到了我們在MainConfig中使用@ConponentScans 中新增的元資訊, 如value=com.changwu
// basePackages lazyInit userDefualtFileter ,,, includeFileters excludeFilters scopeResolver nameGenerate ,,,
// Process any @ComponentScan annotations
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
// 為什麼要迴圈,以為 @ComponentScans(value={1,2,3}) value是一個數組
for (AnnotationAttributes componentScan : componentScans) {
// The config class is annotated with @ComponentScan -> perform the scan immediately
// 掃描com.changwu下面的普通類, 也就是添加了@Component註解的類, 然後將掃描出來的bean放到map中
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// Check the set of scanned definitions for any further config classes and parse recursively if needed
//檢查掃描出來的類當中是否還有configuration
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
//檢查 看看被掃描的普通類有沒有新增 配置相關的註解
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
processImports(configClass, sourceClass, getImports(sourceClass), true);
// Process any @ImportResource annotations
AnnotationAttributes importResource =
...
跟進上面的processImports(configClass, sourceClass, getImports(sourceClass), true);
方法,看他如何處理@Import
註解,通過下面的程式碼不難看出@Import
註解存在三種情況,分別是
- ImportSelector
- ImportBeanDefinitionRegistrar
- 普通類
第一種情況處理ImportSelector
, 這個ImportSelector
是很好用的元件,首先第一點: 我們可以通過自動ImportSelector
完成類的批量注入,但是吧這個功能感覺就像是雞肋,棄之可惜,食之無味, 其實他還有一個妙用!配合jdk的動態代理我們可以實現類似AOP的切面,針對某一個物件進行動態的代理, 舉個例子: 自定義一個類,實現BeanPostProcessor
介面,然後重寫它的public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { ... }
然後根據使用者名稱進行判斷,當找我們指定的使用者時,我們可以使用JDK的動態代理完成將這個物件轉換成代理物件,進而實現切面的增強
看一下它的處理,它判斷出@Import
中含有ImportSelector.class
時,就通過反射將這個物件創建出來代理物件,狸貓換太子,把代理物件交給Spring,得到ImportSelector
的物件,具體反射出來的物件的例項就是程式元自定義的那個ImportSelector
,得到這個物件之後,然後呼叫它的selectImports()
方法,就返回了程式設計師指定的想批量匯入DaoIOC中的物件的全類名, 下一步就是將這些類注入到IOC中,Spring的做法是遞迴呼叫, 因為上面說了,當前方法可以實現的三種Bean的注入,一般來說,通過 ImportSelecor
匯入的類就是普通類了, 會進入下面程式碼中的最後一個else語句塊
第二種情況,處理ImportBeanDefinitionRegistrar
,Spring的做法是,將它新增進一個map中
this.importBeanDefinitionRegistrars.put(registrar, importingClassMetadata);
第三種情況,同樣和第一種情況是一樣的,也是先將資訊放到map中
this.configurationClasses.put(configClass, configClass);
原始碼如下:
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
// 如果沒有添加了@Implot註解的類,直接退出去
if (importCandidates.isEmpty()) {
return;
}
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
this.importStack.push(configClass);
try {
for (SourceClass candidate : importCandidates) {
// 情況1: 處理 @ImportSelector 註解
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
// 被迴圈獲取出現在Spring自己的以及掃描出來的全部的物件的Class描述
Class<?> candidateClass = candidate.loadClass();
// 只要這個物件的@Import(value= ImportSelector.clas)就被命中
// 反射實現一個物件,反射建立的這個物件就是我們的手動新增的 繼承 ImportSelector 的那個物件
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
ParserStrategyUtils.invokeAwareMethods(
selector, this.environment, this.resourceLoader, this.registry);
if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
this.deferredImportSelectors.add(
new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
} else {
// 回撥反射出來的這個物件啊的 SelectImports() 方法,就能動態的獲取出我們手動新增進去的,準備批量注入的 物件的 ClassName 陣列
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
// 將importClassNames新增進一個list -- annotatedClasses中,然後返回出來
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
// 遞迴呼叫, processImports()方法,顯然,再次遞迴的話,傳遞進去的importSourceClasses就是當前的類, 如果當前類是普通類,遞迴時就不再來到這裡了, 而是進入下面的else程式碼塊
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
} // 情況2: 處理@ImportBeanDefinitionRegistrar
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
ParserStrategyUtils.invokeAwareMethods(
registrar, this.environment, this.resourceLoader, this.registry);
// 沒有和上面一樣進行回撥,而是放入到一個list中
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
// 情況3: 處理普通類
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
// 否則,加入到importStack後呼叫 processConfigurationClass 進行處理
//processConfigurationClass() 方法就在下面, 裡面主要就是把類放到configurationClasses
//configurationClasses是一個集合,會在後面拿出來解析成bd繼而註冊
//可以看到普通類在掃描出來的時候就被註冊了
//如果是importSelector,會先放到configurationClasses後面進行出來註冊
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass));
}
}
}
當處理完成了三種@Import
型別的匯入方式之後,我們繼續往下看,三種方式都是忙著把讀取讀來的資訊往map中放,那麼在哪裡進行處理的呢? 思考一下,接下來是不是得將讀取到的資訊註冊進IOC中? 沒錯,我們退會到ConfigurationClassPostProcessor
中的this.reader.loadBeanDefinitions(configClasses);
方法中
同樣經過幾個沒有重要邏輯的方法之後,我們來到了ConfigurationClassBeanDefinitionReader
中,著重它的loadBeanDefinitionsForConfigurationClass()
方法, 原始碼我貼在下面:
看看他做了什麼, 一目瞭然,很清晰的思路,很牛逼很牛逼!!!
如果Spring發現,當前的類是被匯入進來的,他按照Bean匯入進來的方式進行註冊Bean,如果進給看一下,就能看熟悉的一幕,Spring使用Register
進行Bean的註冊
如果Spring發現它有BeanMethod,也就是發現這個物件存在方法,換句話說發現我們的物件存在方法,就會進一步解析我們的方法,怎麼解析方法呢? 按照物件的方法和類的方法分別解析,這也是為什麼,當我們在配置類的靜態方法中使用@Bean進行注入物件,即使已經為MainConfig生成了代理,依然會出現重複注入物件的情況,但是BeanName不一樣哦,如果是靜態方法+@Bean, BeanName是當前的方法名
接下來的邏輯是 解析XML與處理Registrar
private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
// 如果一個類是被 Import 進來的, 會在Spring進行標記,然後再這裡完成註冊
// @Import(aaa.class) 那這個aaa就是被Import的,在這裡完成註冊
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
// xml
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
// 處理註冊Registrar 的邏輯
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
程式碼讀到這裡其實已經深入很多層了,重要的邏輯也都過了一下,現在的工作就是層層的往回出棧,回到開始的PostProcessorRegistationDelegate
中的invokeBeanFactoryPostProcessors()
方法
上面的大部分篇幅都是當前方法中的 invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
方法層層遞迴進去的,現在我們從它開始往後看
剛開始不是說,一共是建立三個list嗎?然後又把其中的兩個進行了合併了, 那麼接下來的工作就是去執行這兩個list中剩下的沒執行的邏輯, 沒執行的就是,Spring自己提供的和程式設計師新增的BeanFactoryPostProcessor
的實現,沒錯就是執行重寫的BeanFactoryPostProcessor()
的postProcessBeanFactory()
方法
// registryProcessors 其實就是唯一的 ConfigurationClassPostProcessor
invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
// 自定義BeanFactoryPostProcessor 的 postProcessorBeanFactory()方法
invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
大家看,上面的兩個方法是一樣,只不過是傳遞進去的引數不一樣而已,其實吧,高潮來了,如果大家還記得的話,應該猜到了現在的入參位置上的引數, 沒錯就是 ConfigurationClassPostProcessor
這個類, 它太牛逼了! 同時實現了BeanDefinitionRegistryPostProcessor
和BeanFactoryPostProcessor
的抽象方法, 下面就去具體看一下它的實現,準備好了嗎? 來高潮了哦
原始碼如下: 它的解析我寫在下面
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
int factoryId = System.identityHashCode(beanFactory);
if (this.factoriesPostProcessed.contains(factoryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + beanFactory);
}
this.factoriesPostProcessed.add(factoryId);
if (!this.registriesPostProcessed.contains(factoryId)) {
// BeanDefinitionRegistryPostProcessor hook apparently not supported...
// Simply call processConfigurationClasses lazily at this point then.
processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
}
enhanceConfigurationClasses(beanFactory);
beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}
enhanceConfigurationClasses(beanFactory);
這是在幹什麼? 跟大家說,這太牛了!!!為什麼說它牛? 不買關子,它在這裡開啟了JDK的動態代理
在這個方法中有一段判斷邏輯,如下: 這是很讚的一段程式碼,感覺到了心跳的加速! 它判斷當前的這個BeanDefinition
是不是full型別的, 關於這個Full的解釋,其實我們上面的描述中有說過,就是說,如果我們的MainConfig
添加了@Configuration
註解,它就被會標記為FUll, 被標記為full的話,就會在下面的程式碼中產生cglib的動態代理,也就是說,我們獲取到的存入容器的MainConfig
可以不是普通的原始物件, 而是被Cglib增強後的物件, 這有什麼用呢? 用處可大了! 我們通常會在配置類中新增@Bean註解,注入物件,但是如果被添加了@Bean註解的方法彼此之間相互呼叫的戶,就會出現重複注入的現象,Spring通過下面的判斷,進行代理,不再給使用者原始的Mainconfig
,這樣就實現對方法呼叫的控制,進而保證了百分百單例的情況
public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
for (String beanName : beanFactory.getBeanDefinitionNames()) {
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
// 判斷isFull, 看看是不是添加了@Configuration的全註解的類
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef)) {
if (!(beanDef instanceof AbstractBeanDefinition)) {
throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" +
beanName + "' since it is not stored in an AbstractBeanDefinition subclass");
}
else if (logger.isWarnEnabled() && beanFactory.containsSingleton(beanName)) {
logger.warn("Cannot enhance @Configuration bean definition '" + beanName +
"' since its singleton instance has been created too early. The typical cause " +
"is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor " +
"return type: Consider declaring such methods as 'static'.");
}
// 如果是的話,放到這個linkedHashMap中
configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
}
}
接著往下看程式碼就可以看到Spring底層使用原生cglib進行代理的邏輯了,
private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(configSuperClass);
enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
enhancer.setUseFactory(false);
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
enhancer.setCallbackFilter(CALLBACK_FILTER);
enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
return enhancer;
}
這個過程中有幾個需要注意的地方,一般我們自己實現Cglib時, 都只是設定一個setSuperclass(XXX)
然後對這個XXX進行增強,但是Spring沒這麼簡單,它還enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
想想,為什麼還要這個介面呢?看下面的圖
通過上面的圖可以看到,這個介面實現了beanFactoryAware
,而這個beanFactory
中存在setBeanFactory(BeanFactory bf)
怎麼樣? 有思路沒?
整理一下思路,就是說,Spring的目的就是將程式設計師傳遞進來的MainConfig
進行動態代理,為啥要代理呢? 因為有的程式設計師會故意搞破壞,會使用被@Bean
標註的方法之間相互呼叫,導致Bean的多次注入,於是Spring想通過代理,返回給使用者一個代理物件,然後新增動態的判斷, 如果容器中已經存在bean了,就從容器中獲取,不再重複注入,沒有的話就注入進去一個
這就引出了為什麼,代理物件需要一個BeanFactory
,因為BeanDefinition
都在BeanFactory中,這也是為什麼上面需要setInterface()
接著enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
設定一個生成策略, 因為我們生曾的代理類需要一個BeanFactory型別的變數啊,沒有這個引用,如何接受前面set的BeanFactory???
再往後的亮點就是enhancer.setCallbackFilter(CALLBACK_FILTER);
設定回撥的攔截器,看看有哪些攔截器呢? 程式碼如下:
private static final Callback[] CALLBACKS = new Callback[] {
// 第一個實現, 增強方法, 主要控制bean的作用域換句話說就是讓每一次呼叫方法不再去new, 跟進去看看
new BeanMethodInterceptor(), // 他是當前類的內部類
//設定一個beanFactory
new BeanFactoryAwareMethodInterceptor(),
NoOp.INSTANCE
};
我們跟進去BeanMethodInterceptor
,這個類也很精彩,玩過cglib的人都知道需要一個inteceptor,而我們正在看的這個介面就實現了methodInterceptor
,重不重要,你懂的...
直接看它的intercept()
方法, 細細品味這個方法,很有味道哦!!!, 它的解析我寫在這個方法的下面
public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
MethodProxy cglibMethodProxy) throws Throwable {
// enhancedConfigInstance 是代理物件
// 通過代理物件enhancedConfigInstance中cglib生成的成員變數$$beanFactory獲得beanFactory。
ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);
// Determine whether this bean is a scoped-proxy
Scope scope = AnnotatedElementUtils.findMergedAnnotation(beanMethod, Scope.class);
if (scope != null && scope.proxyMode() != ScopedProxyMode.NO) {
String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
beanName = scopedBeanName;
}
}
if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX + beanName) &&
factoryContainsBean(beanFactory, beanName)) {
Object factoryBean = beanFactory.getBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName);
if (factoryBean instanceof ScopedProxyFactoryBean) {
// Scoped proxy factory beans are a special case and should not be further proxied
}
else {
return enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName);
}
}
if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
// The factory is calling the bean method in order to instantiate and register the bean
// (i.e. via a getBean() call) -> invoke the super implementation of the method to actually
// create the bean instance.
if (logger.isWarnEnabled() &&
BeanFactoryPostProcessor.class.isAssignableFrom(beanMethod.getReturnType())) {
logger.warn(String.format("@Bean method %s.%s is non-static and returns an object " +
"assignable to Spring's BeanFactoryPostProcessor interface. This will " +
"result in a failure to process annotations such as @Autowired, " +
"@Resource and @PostConstruct within the method's declaring " +
"@Configuration class. Add the 'static' modifier to this method to avoid " +
"these container lifecycle issues; see @Bean javadoc for complete details.",
beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName()));
}
// 滿足條件 呼叫父類的構造方法new 物件
return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
}
return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
}
首先,它從代理物件中獲取出beanFactory
然後他處理 FactoryBean
的情況,這個FactoryBean
太牛了,因為當程式設計師把一個FactoryBean
注入到IOC時,附帶的還會把另一個物件駐入進IOC, 它是如何進行區分判斷的呢? Spring會使用一個BeanFactory.FACTORY_BEAN_PREFIX == &
這個字首去匹配, 比如userDao3()中呼叫了userDao4(), 他就是用&userDao4當成key去beanFactory中獲取,如果獲取獲取出物件了,說明這是個FactoryBean
需要對獲取出來的這個物件進一步生成代理
接下來判斷,是new 呢? 還是從Factory中獲取呢?
Spring的判斷依據是根據方法名,判斷呼叫方法和正在執行的方法是同一個方法,根據什麼呢? 只要名字相同, 結論就是直接new
舉個例子:
userDao3(){}
// 它的呼叫方法和正在執行的方法是同一個方法,怎麼相同呢? 名字相同, 結論就是直接new
userDao4(){
userDao3()
}
userDao4()是呼叫方法, userDao3()執行方法 userDao4 和 userDao3 名字不一樣, 所以選擇getBean()
第二個例子:
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("intercept.............");
methodProxy.invokeSuper(o,objects);
return null;
}
比如我們僅僅執行代理方法A, 這個A方法 就時上面的method 也是methodProxy, 但是如果我們在代理方法A中執行B方法, 這時 A == method != methodProxy == B
程式碼看到這裡,其實一開始的refresh()
中的invokeBeanFactoryPostProcessors(beanFactory);
方法就看完了, 著呢麼樣刺激不?
有錯誤的話歡迎批評指出,有過對您有幫助,歡迎點贊支