程序入口:
接着上一篇博客中看完了在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);
方法就看完了, 着呢么樣刺激不?
有錯誤的話歡迎批評指出,有過對您有幫助,歡迎點贊支持