主頁 > 後端開發 > Spring原始碼分析之`BeanFactoryPostProcessor`呼叫程序

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

2020-10-27 17:47:12 後端開發

前文傳送門:

  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 = https://www.cnblogs.com/tomakemyself/p/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-面試輪次 , 騰訊-乘車碼-面試輪次 , 騰訊-微信支付-面試輪次 , 騰訊-零售新業務-面試輪次 , 騰訊-直播平臺-面試輪次, 快手-廣告業務部-面試輪次 , 貝殼找房-商品組-面試輪次 , 百度-資訊流-面試輪次 , 京東-零售-面試輪次 , 京東-物流-面試輪次 , 京東-電商-面試輪次 , 滴滴-小桔車服-面試輪次 , 滴滴-金融-面試輪次 , 阿里-高德-面試輪次 , 阿里-大文娛-面試輪次 , 阿里-健康-面試輪次 , 阿里-螞蟻金服-面試輪次 , 美團-外賣-面試輪次 , 美團-風控-面試輪次

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/193862.html

標籤:Java

上一篇:用C語言驗證 “6174黑洞之謎”!萬物始于C,編程世界的黑洞!

下一篇:面試題(2020)微信小程式常見面試題

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more