Spring原始碼分析之`BeanFactoryPostProcessor`呼叫過程

雕爺的架構之路發表於2020-10-27

前文傳送門:

  1. Spring原始碼分析之預啟動流程
  2. Spring原始碼分析之BeanFactory體系結構

本文內容:

  1. AbstractApplicationContext#refresh前部分的一點小內容
  2. BeanFactoryPostProcessor呼叫過程詳解
  3. mybatis是如何使用本節知識整合spring的?

正文:

在Spring中,一共分為BeanFactoryPostProcessorBeanPostProcessor兩類後置處理器,他們主要的職責如下:

  • BeanFactoryPostProcessor:負責beanClassbeanDefinition的過程,包括但不限於尋找合適的beanClass,建立beanDefinition,修改beanDefinition,將beanDefinition註冊到BeanFactory
  • BeanPostProcessor:負責beanDefinitionbean的過程,包括但不限於bean的屬性賦值,初始化

本次主要分析BeanFactoryPostProcessor的呼叫過程,下面是BeanFactoryPostProcessor呼叫過程的大體流程圖,也是本文想要表述的大概內容,原圖連結: BeanFactoryPostProcessor呼叫過程

img

refresh的前半段流程

// 啟動前的準備工作
prepareRefresh();
// 由於web專案中並不會先引入DefaultListableBeanFactory,在這裡通知子類重新整理BeanFactory
// 而我們是使用new AnnotationConfigApplicationContext()的方式,就是直接返回之前引入的DefaultListableBeanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 準備工作,給DefaultListableBeanFactory填充屬性
prepareBeanFactory(beanFactory);
// 留於子類呼叫的擴充套件方法
postProcessBeanFactory(beanFactory);
// 呼叫實現BeanFactoryPostProcessor的後置處理器,
// 其實就是我們在new AnnotatedBeanDefinitionReader時註冊的解析配置類的後置處理器ConfigurationClassPostProcessor
// 這裡會解析配置類以及處理解析配置類後所引入的所有BeanFactoryPostProcessor
invokeBeanFactoryPostProcessors(beanFactory);
// 註冊上一步解析出來的所有的BeanPostProcessor
// 註冊邏輯和上一步大致相同,PriorityOrdered-> Ordered -> 普通的
registerBeanPostProcessors(beanFactory);

prepareRefresh

// 設定容器狀態
this.closed.set(false);
this.active.set(true);

prepareBeanFactory

// 新增一個ApplicationContextAwareProcessor,用於bean初始化前呼叫一系列的Aware介面回撥
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
// 用於處理實現ApplicationListener介面的bean,bean初始化後新增監聽
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

invokeBeanFactoryPostProcessors(重點)

此方法將解析配置類以及處理解析配置類後所引入的所有BeanFactoryPostProcessor

溫馨提醒:內容較多,還請耐心閱讀~

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    //由於之前註冊的都是BeanDefinition,此時還並沒有生產任何的BeanFactoryPostProcessor,所以getBeanFactoryPostProcessors是空的
    PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

}

invokeBeanFactoryPostProcessors

前部分主要是尋找ConfigurationClassPostProcessor並將它例項化

//放置已處理的beanName
Set<String> processedBeans = new HashSet<>();
//放置常規的後置處理器,就是隻實現了BeanFactoryPostProcessor介面的
List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
//放置實現了BeanDefinitionRegistryPostProcessor介面的,之前我們註冊的後置處理器中只有ConfigurationClassPostProcessor實現了
List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
//放置當前的RegistryProcessors
List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
//查詢實現了BeanDefinitionRegistryPostProcessor介面的BeanName,其實就只有一個ConfigurationClassPostProcessor
String[] postProcessorNames =
    beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
    //ConfigurationClassPostProcessor同樣實現了PriorityOrdered介面
    if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
        //注意這裡呼叫了getBean方法,生產了ConfigurationClassPostProcessor,放到currentRegistryProcessors集合中
        currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
        processedBeans.add(ppName);
    }
}
//排序
sortPostProcessors(currentRegistryProcessors, beanFactory);
//將生產出來的後置處理器放到集合中
registryProcessors.addAll(currentRegistryProcessors);

接下來就開始呼叫ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法

//呼叫ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);

private static void invokeBeanDefinitionRegistryPostProcessors(
    Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {
	//迴圈BeanDefinitionRegistryPostProcessor進行呼叫
    for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
        postProcessor.postProcessBeanDefinitionRegistry(registry);
    }
}

postProcessBeanDefinitionRegistry

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    processConfigBeanDefinitions(registry);
}
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
	//放置候選配置類
    List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
    String[] candidateNames = registry.getBeanDefinitionNames();
    //遍歷之前註冊的所有bean定義,找到其中的配置類,其實就是我們自己傳進來的配置類
    for (String beanName : candidateNames) {
        //...省略校驗過程...
        BeanDefinition beanDef = registry.getBeanDefinition(beanName);
        //檢查是否是有@Configuration註解的BeanDifinition -> full型別的配置類 -> 會把配置類替換成動態代理類
        //或者該類包含@Component @ComponentScan @Import @ImportResource @Bean 註解的其中之一 -> lite型別的配置類  -> 不會替換成動態代理類
        else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
            configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
        }
    }
    //....省略片段....
    //例項化一個配置類解析器
    ConfigurationClassParser parser = new ConfigurationClassParser(
        this.metadataReaderFactory, this.problemReporter, this.environment,
        this.resourceLoader, this.componentScanBeanNameGenerator, registry);
    
    do {
        //解析配置類
        parser.parse(candidates);
        parser.validate();
        //parser.getConfigurationClasses()就是拿到剛剛解析完放到map中的配置類
        Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
        configClasses.removeAll(alreadyParsed);
        
        //這裡處理@Import匯入的beanDefintion和配置類中的@Bean
        this.reader.loadBeanDefinitions(configClasses);
        alreadyParsed.addAll(configClasses);
		//以下邏輯是找出未解析的配置類,如@Bean和ImportBeanDefinitionRegistrar所引入的
        candidates.clear();
        if (registry.getBeanDefinitionCount() > candidateNames.length) {
            String[] newCandidateNames = registry.getBeanDefinitionNames();
            Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
            Set<String> alreadyParsedClasses = new HashSet<>();
            for (ConfigurationClass configurationClass : alreadyParsed) {
                alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
            }
            for (String candidateName : newCandidateNames) {
                if (!oldCandidateNames.contains(candidateName)) {
                    BeanDefinition bd = registry.getBeanDefinition(candidateName);
                    //將是配置類並且沒有解析過的BeanDefinition放到候選集合中繼續解析
                    if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
                        !alreadyParsedClasses.contains(bd.getBeanClassName())) {
                        candidates.add(new BeanDefinitionHolder(bd, candidateName));
                    }
                }
            }
            candidateNames = newCandidateNames;
        }
    }
    while (!candidates.isEmpty());
}

ConfigurationClassUtils.checkConfigurationClassCandidate中的摘取片段

//檢查是否有標識@Configuration
Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
    //設定配置屬性值為full
    beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
}
//檢查是否包含@Component @ComponentScan @Import @ImportResource @Bean
else if (config != null || isConfigurationCandidate(metadata)) {
    //設定配置屬性值為lite
    beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
}
else {
    return false;
}

配置類解析流程(parser.parse(candidates))

public void parse(Set<BeanDefinitionHolder> configCandidates) {
    for (BeanDefinitionHolder holder : configCandidates) {
        BeanDefinition bd = holder.getBeanDefinition();
        //配置類的beanDefinition為AnnotatedGenericBeanDefinition,true
        if (bd instanceof AnnotatedBeanDefinition) {
            //傳入配置類的後設資料與beanName
            parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
        }
    }
}
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
    processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER);
}

processConfigurationClass

protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
    SourceClass sourceClass = asSourceClass(configClass, filter);
    do {
        //解析配置類,這裡可能返回配置類的父類,需要繼續處理
        sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
    }
    while (sourceClass != null);
    //將配置類放入map中
    this.configurationClasses.put(configClass, configClass);
}

doProcessConfigurationClass

//@Configuration 本身也是 @Component的組合註解
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
    // 處理內建類,如果內建類也是個配置類,遞迴處理內建類
    processMemberClasses(configClass, sourceClass, filter);
}

處理@ComponentScan

// Process any @ComponentScan annotations
// 找出配置類上的@ComponentScan註解屬性
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
    sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);

for (AnnotationAttributes componentScan : componentScans) {
    //將@ComponentScan引入的所有類掃描成BeanDefinition並註冊到容器中
    Set<BeanDefinitionHolder> scannedBeanDefinitions =
        this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
    //這裡迴圈是為了判斷掃描出來的beanDefinition是否是配置類,如果是配置類的話需要遞迴解析
    for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
        BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
        //這裡是必然為true, 能被掃描出來的必然有@Component註解,而@Component註解為lite配置類
        //這裡主要是為了在檢查的同時設定一下full或者lite的型別
        if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
            //配置類就繼續遞迴解析
            parse(bdCand.getBeanClassName(), holder.getBeanName());
        }
    }
}
this.componentScanParser.parse
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
    //重新new了一個classpath的bean定義掃描器,沒用我們最開始建立的
    // 這裡新增了一個預設的過濾器,過濾@Component註解的
    ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
                                                                        componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
    //新增自己配置的過濾器
    for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
        for (TypeFilter typeFilter : typeFiltersFor(filter)) {
            scanner.addIncludeFilter(typeFilter);
        }
    }
    for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
        for (TypeFilter typeFilter : typeFiltersFor(filter)) {
            scanner.addExcludeFilter(typeFilter);
        }
    }
    //如果配置的為懶載入,則掃描出來的所有BeanDefinition都預設為懶載入的
    boolean lazyInit = componentScan.getBoolean("lazyInit");
    if (lazyInit) {
        scanner.getBeanDefinitionDefaults().setLazyInit(true);
    }
    //將配置的basePackages中所有的包路徑放到set集合中,保證最終所有的包路徑唯一
    Set<String> basePackages = new LinkedHashSet<>();
    String[] basePackagesArray = componentScan.getStringArray("basePackages");
    for (String pkg : basePackagesArray) {
        String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
                                                               ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
        Collections.addAll(basePackages, tokenized);
    }
    //新增一個排除過濾器,排除該配置類
    scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
        @Override
        protected boolean matchClassName(String className) {
            return declaringClass.equals(className);
        }
    });
    //開始掃描
    return scanner.doScan(StringUtils.toStringArray(basePackages));
}
doScan
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 -> 預設過濾器為過濾標識了@Component註解的class
        Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
        for (BeanDefinition candidate : candidates) {
            String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
            if (candidate instanceof AbstractBeanDefinition) {
                //設定預設值,比如上一個方法剛剛設定的是否懶載入
                postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
            }
            if (candidate instanceof AnnotatedBeanDefinition) {
                //解析beanClass的所有註解填充到beanDefinition中,@Lazy @Primary @DependsOn @Role @Description
                AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
            }
            //檢查之前是否註冊過,未註冊返回true
            if (checkCandidate(beanName, candidate)) {
                BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                beanDefinitions.add(definitionHolder);
                //將beanDefinition註冊到容器中
                registerBeanDefinition(definitionHolder, this.registry);
            }
        }
    }
    return beanDefinitions;
}
尋找候選元件#findCandidateComponents
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
    return scanCandidateComponents(basePackage);
}
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
    Set<BeanDefinition> candidates = new LinkedHashSet<>();
    //將類路徑替換成絕對路徑
    String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
        resolveBasePackage(basePackage) + '/' + this.resourcePattern;
    //找出該路徑下的所有類資源
    Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
    for (Resource resource : resources) {
        if (resource.isReadable()) {
            MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
            //呼叫剛剛配置的過濾器進行匹配,
            //預設過濾器邏輯:是否標識了@Component註解(包括組合的,如@Service)
            if (isCandidateComponent(metadataReader)) {
                //通過掃描方式建立的BeanDefintion為ScannedGenericBeanDefinition
                ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                sbd.setSource(resource);
                //不是介面和抽象類,或者是抽象類但標識了@Lookup
                if (isCandidateComponent(sbd)) {
                    //將beanDefinition存到集合中
                    candidates.add(sbd);
                }
            }
        }
    }
    return candidates;
}

處理@Import

processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
                            Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
                            boolean checkForCircularImports) {
//importCandidates為@Import中的value陣列
    for (SourceClass candidate : importCandidates) {
        if (candidate.isAssignable(ImportSelector.class)) {
            // Candidate class is an ImportSelector -> delegate to it to determine imports
            Class<?> candidateClass = candidate.loadClass();
            //例項化我們寫的實現ImportSelector介面的類
            ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
                                                                           this.environment, this.resourceLoader, this.registry);
            //呼叫selectImports方法返回我們需要注入到容器中bean陣列
            String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
            //轉為SourceClass集合
            Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
            //再次遞迴呼叫本方法,如果我們返回的陣列是一些沒有實現Import相關介面的類,
            //就會走到最後的else邏輯,當成配置類處理
            processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
        }
        //這裡就走實現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 =
                ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
                                                     this.environment, this.resourceLoader, this.registry);
            //這裡先把Registrar放到配置類的importBeanDefinitionRegistrars屬性中,最後解析完呼叫loadBeanDefinition進行處理
            configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
        }
        else {
            //普通的bean當做配置類處理
            processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
        }
    }
}

處理@Bean

// 將配置類中@Bean的方法解析成方法後設資料放到配置類中
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
    configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}

到這裡配置類的主要解析流程就已經結束了,接下來回到解析之後的流程

處理@Import匯入的beanDefintion和配置類中的@Bean

this.reader.loadBeanDefinitions(configClasses);
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
    TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
    //迴圈剛剛解析過的所有配置類
    for (ConfigurationClass configClass : configurationModel) {
        loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
    }
}
private void loadBeanDefinitionsForConfigurationClass(
    ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {

    if (trackedConditionEvaluator.shouldSkip(configClass)) {
        String beanName = configClass.getBeanName();
        if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
            this.registry.removeBeanDefinition(beanName);
        }
        this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
        return;
    }
    // 將Import註解引入的class註冊到容器的BeanDefinitionMap中
    if (configClass.isImported()) {
        registerBeanDefinitionForImportedConfigurationClass(configClass);
    }
    for (BeanMethod beanMethod : configClass.getBeanMethods()) {
        //將beanMethod轉化成BeanDefinition註冊到容器的beanDefinitionMap中
        loadBeanDefinitionsForBeanMethod(beanMethod);
    }

    loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
    //呼叫在解析Import時放入的ImportBeanDefinitionRegistrar的registerBeanDefinitions方法
    loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
摘取處理BeanMethod邏輯如下
private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
    ConfigurationClass configClass = beanMethod.getConfigurationClass();
    MethodMetadata metadata = beanMethod.getMetadata();
    String methodName = metadata.getMethodName();

    //解析出方法上@Bean註解的所有屬性值
    AnnotationAttributes bean = AnnotationConfigUtils.attributesFor(metadata, Bean.class);
    //建立一個ConfigurationClassBeanDefinition,標識為通過@Bean註解註冊的bean
    ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata);
    beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));
    //以下邏輯為拿出@Bean中的屬性填充到BeanDefinition中,最後註冊容器中
    beanDef.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
    //解析註解填充屬性
    AnnotationConfigUtils.processCommonDefinitionAnnotations(beanDef, metadata);

    Autowire autowire = bean.getEnum("autowire");
    if (autowire.isAutowire()) {
        beanDef.setAutowireMode(autowire.value());
    }
    boolean autowireCandidate = bean.getBoolean("autowireCandidate");
    if (!autowireCandidate) {
        beanDef.setAutowireCandidate(false);
    }
    String initMethodName = bean.getString("initMethod");
    if (StringUtils.hasText(initMethodName)) {
        beanDef.setInitMethodName(initMethodName);
    }
    String destroyMethodName = bean.getString("destroyMethod");
    beanDef.setDestroyMethodName(destroyMethodName);
	//將建立的BeanDefinition註冊到容器中
    this.registry.registerBeanDefinition(beanName, beanDefToRegister);
}

以上,ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法大致過程就這些了,接下來回到剛開始的invokeBeanFactoryPostProcessors方法

invokeBeanFactoryPostProcessors

處理實現了Ordered介面的BeanDefinitionRegistryPostProcessor

//...省略之前程式碼片段
//呼叫ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
//清空,以便處理後面的後置處理器
currentRegistryProcessors.clear();

//再次查詢實現了BeanDefinitionRegistryPostProcessor介面的BeanName,這裡就是從配置類中解析出來的一些
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
    //不包括已經處理過的,並且先處理實現Ordered介面的
    if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
        currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
        processedBeans.add(ppName);
    }
}
//根據Ordered排序
sortPostProcessors(currentRegistryProcessors, beanFactory);
//將後置處理器放到已註冊的集合中
registryProcessors.addAll(currentRegistryProcessors);
//呼叫所有後置處理器的postProcessBeanDefinitionRegistry方法
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
//再次清理,因為後面還要處理未實現Ordered介面的
currentRegistryProcessors.clear();

最後需要迴圈處理剩下的所有後置處理器,因為可能從剩下的後置處理器中又解析出新的後置處理器

//下面的邏輯和上面的一模一樣,while迴圈處理所有剩下的後置處理器,直到全部處理完畢
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();
}

呼叫所有後置處理器的postProcessBeanFactory方法

/**
* 呼叫所有後置處理器的postProcessBeanFactory方法,
* 如果自己沒實現的話,Spring中只有一個內建的ConfigurationClassPostProcessor
* ConfigurationClassPostProcessor中的postProcessBeanFactory方法主要是將配置類換成動態代理
*/
invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);

ConfigurationClassPostProcessor#postProcessBeanFactory

public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
	//將所有配置類進行動態代理,這樣@Bean中依賴其他的Bean就可以從容器中拿bean了
	/**
	 * example:
	 * @Bean
	 * public Car car(){
	 *     return new Car(wheel());
	 * }
	 * @Bean
	 * public Wheel wheel(){
	 *     return new Wheel();
	 * }
	 * 如果配置類不換成動態代理的話,每次從容器中拿car都將new一個wheel
	 * 注意,這裡只有full型別的配置類才會生成代理類,lite型別的不會,
	 * 所以lite型別的配置類每次獲取car都會生成一個wheel
	 */
	enhanceConfigurationClasses(beanFactory);
	//新增一個beanPostProcessor
	beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}

最後處理實現了BeanFactoryPostProcessor介面的後置處理器

//處理方式與BeanDefinitionRegistryPostProcessor相同
//找出所有實現了BeanDefinitionRegistryPostProcessor的後置處理器
String[] postProcessorNames =
				beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
//先處理實現PriorityOrdered介面的
invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
//在處理實現Ordered介面的
invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
//最後處理普通的
invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);

到這裡,關於BeanFactoryPostProcessor呼叫過程就已經完結了

關於BeanDefinition的小彩蛋

解析配置類的流程我們已經分析完了,那麼在這過程中用了多少種BeanDefinition呢?他們對應的型別又是什麼呢?這裡附上本文的一個小彩蛋。

  • AnnotatedGenericBeanDefinition:在開始傳入的配置類,以及通過@Import註解引入的Bean
  • ScannedGenericBeanDefinition:通過@Component掃描包引入的Bean
  • ConfigurationClassBeanDefinition:通過@Bean註解引入的Bean
  • RootBeanDefinition:Spring內部使用,如生產Bean時將其他BeanDefinition轉成RootBeanDefinition

Mybatis 如何整合 Spring的?

此節知識為概要知識,具體內容將放在Mybatis原始碼系列詳細說明

先帶大家理理思路~

我們知道,在Spring中是可以通過掃描的方式掃描出標識了@Component註解的class註冊到容器中,並且該class不能為一個介面類(忘了請看上面的掃描邏輯),而我們的mapper通常又是一個介面類,這是預設不允許被註冊的。那麼該如何解決這個問題呢?

思考:既然預設不允許是介面類,那麼我們是否可以自定義一個掃碼器繼承Spring的掃描器,然後重寫其中判斷是否為介面類的邏輯,這樣,我們不就可以使用我們自定義的掃描器去掃描包就可以了嗎?

問題2:假設上面的方法可行,但是我們掃描出來的BeanDefintion是個介面,介面是不能被例項化的,那在後面我們createBean中的例項化步驟又該如何解決呢?

思考:我們知道其實我們的mappermybatis中本來就是個介面,我們建立時是通過sqlSessionTemplate.getMapper()的方式建立的,這裡其實是生成了一個代理類返回給我們,那我們應該如何將這個代理類給接到Spring的createBean過程中呢,如何接過去了豈不是就萬事大吉?

小知識:嘿,不知道大家還記不記的我們的bean裡有一種特殊的bean稱為FactoryBean,我們這個FactoryBean最後從容器中獲取出來時其實是先拿到這個FactoryBean,然後呼叫它的getObject()方法返回我們真正需要的bean

思考:知道這個之後,那麼我們是不是可以使用FactoryBean,然後將掃描出來的介面(mapper)放到FactoryBean的屬性中,最後從容器中獲取時只要這樣:

public class FactoryBean{
    private Class mapper;
    public Object getObject(){
        sqlSessionTemplate.getMappper(mapper);
    }
}

嘿,看看是不是好像搞定啦~

現在問題好像都已經解決了,那剩下的就是怎麼讓Spring在啟動的時候呼叫我們的自定義掃描器呢?我們現在就來看看原始碼吧

@MapperScan

Mybatis整合Spring當然是從@MapperScan註解看起,因為我們通常情況只加這個註解就可以了

@MapperScan簡要內容如下

// 組合註解,組合了@Import註解,再通過@Import註解匯入了MapperScannerRegistrar類
@Import(MapperScannerRegistrar.class)
public @interface MapperScan{
	// 包路徑
	String[] basePackages() default {}
}

MapperScannerRegistrar

// 實現的是ImportBeanDefinitionRegistrar介面
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware{
    
}

registerBeanDefinitions

@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
	// 從後設資料中拿到@MapperScan的資訊
    AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
    // 例項化一個自定義的掃描器
    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
	
    // 下面都是些屬性填充,由於一般我們只配一個包路徑,所以下面除了包路徑,其他都是null
    if (resourceLoader != null) {
        scanner.setResourceLoader(resourceLoader);
    }

    Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
    if (!Annotation.class.equals(annotationClass)) {
        scanner.setAnnotationClass(annotationClass);
    }

    Class<?> markerInterface = annoAttrs.getClass("markerInterface");
    if (!Class.class.equals(markerInterface)) {
        scanner.setMarkerInterface(markerInterface);
    }

    Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
    if (!BeanNameGenerator.class.equals(generatorClass)) {
        scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass));
    }

    Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
    if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
        scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass));
    }

    scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
    scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));

    List<String> basePackages = new ArrayList<String>();
    for (String pkg : annoAttrs.getStringArray("value")) {
        if (StringUtils.hasText(pkg)) {
            basePackages.add(pkg);
        }
    }
    for (String pkg : annoAttrs.getStringArray("basePackages")) {
        if (StringUtils.hasText(pkg)) {
            basePackages.add(pkg);
        }
    }
    for (Class<?> clazz : annoAttrs.getClassArray("basePackageClasses")) {
        basePackages.add(ClassUtils.getPackageName(clazz));
    }
    // 註冊自定義的過濾器,我們啥也沒配,所以掃描出來的所以介面都通過
    scanner.registerFilters();
    // 開始掃描
    scanner.doScan(StringUtils.toStringArray(basePackages));
}

scanner.registerFilters中的有效片段

// 新增一個直接返回true的過濾器
addIncludeFilter(new TypeFilter() {
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        return true;
    }
});

doScan

public Set<BeanDefinitionHolder> doScan(String... basePackages) {
    // 直接走的就是Spring的掃描邏輯了,但現在過濾器只有一個預設全放行的
    Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);

    if (beanDefinitions.isEmpty()) {
        logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
    } else {
		// 處理掃描出來的BeanDefinition,這裡就是我們思考中搞成`FactoryBean`的邏輯
        processBeanDefinitions(beanDefinitions);
    }
    return beanDefinitions;
}

我們思考中重寫的掃描邏輯

@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
    // 放行是介面的類
    return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
}

摘取processBeanDefinitions中的程式碼片段

private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    GenericBeanDefinition definition;
    for (BeanDefinitionHolder holder : beanDefinitions) {
        definition = (GenericBeanDefinition) holder.getBeanDefinition();
        // 將原來的介面mapper放到beanDefintion的構造方法引數中,以指定的構造方法例項化
  definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); 
        // 注意這裡:將原來的beanClass替換成FactoryBean了!
        definition.setBeanClass(this.mapperFactoryBean.getClass());
    }
}

Mybatis整合Spring的過程大致就是這些了

Spring 原始碼系列
  1. Spring原始碼分析之 IOC 容器預啟動流程(已完結)
  2. Spring原始碼分析之BeanFactory體系結構(已完結)
  3. Spring原始碼分析之BeanFactoryPostProcessor呼叫過程(已完結)
  4. Spring原始碼分析之Bean的建立過程
  5. Spring原始碼分析之什麼是迴圈依賴及解決方案
  6. Spring原始碼分析之AOP從解析到呼叫
  7. Spring原始碼分析之事務管理(上),事物管理是spring作為容器的一個特點,總結一下他的基本實現與原理吧
  8. Spring原始碼分析之事務管理(下) ,關於他的底層事物隔離與事物傳播原理,重點分析一下
Spring Mvc 原始碼系列
  1. SpringMvc體系結構
  2. SpringMvc原始碼分析之Handler解析過程
  3. SpringMvc原始碼分析之請求鏈過程
Mybatis 原始碼系列

暫定


追更,可關注我,近期有時間就文章全寫完,分享純粹為了樂趣,也有一種成就感吧,筆者這篇文章先就到這

關注筆者公眾號:奇客時間,獲取網際網路公司面試真題,回覆關鍵字形式:公司-部門-面試輪次,例如 阿里-螞蟻金服-一面,自動回覆面試真題;當前已經收錄如下:

位元組跳動-抖音-面試輪次, 搜狐-搜尋組-面試輪次, OPPO-商城-面試輪次, 58同城-基礎架構部-面試輪次,湖南臺-芒果TV-面試輪次 , 騰訊-乘車碼-面試輪次 , 騰訊-微信支付-面試輪次 , 騰訊-零售新業務-面試輪次 , 騰訊-直播平臺-面試輪次, 快手-廣告業務部-面試輪次 , 貝殼找房-商品組-面試輪次 , 百度-資訊流-面試輪次 , 京東-零售-面試輪次 , 京東-物流-面試輪次 , 京東-電商-面試輪次 , 滴滴-小桔車服-面試輪次 , 滴滴-金融-面試輪次 , 阿里-高德-面試輪次 , 阿里-大文娛-面試輪次 , 阿里-健康-面試輪次 , 阿里-螞蟻金服-面試輪次 , 美團-外賣-面試輪次 , 美團-風控-面試輪次

相關文章