美文网首页js css htmlSpring Boot
【深入浅出Spring原理及实战】「源码调试分析」结合DataS

【深入浅出Spring原理及实战】「源码调试分析」结合DataS

作者: 洛神灬殇 | 来源:发表于2023-01-16 12:58 被阅读0次

    每日一句

    人的一生中不可能会一帆风顺,总会遇到一些挫折,当你对生活失去了信心的时候,仔细的看一看、好好回想一下你所遇到的最美好的事情吧,那会让你感觉到生活的美好


    注入案例代码

    如何通过实现SpringBoot框架带有的ImportBeanDefinitionRegistrar注册器,注入我们想要注册的bean对象实例。只需要采用@Import的注解进行注入对应的一类相关的bean对象。

    @Import({DataSourceRegister.class,A.class})
    @SpringBootApplication
    @ComponentScan("com.libo")
    public class LiboApplication {
        public static void main(String[] args) {
            SpringApplication sa = new SpringApplication(LiboApplication.class);
            sa.run(args);
        }
    }
    

    DataSourceRegister的开发实现

    • 在springboot启动的时候,loader模块会根据“清单文件”加载该Application类,并反射调用psvm入口函数main,@Import注解也可以导入一个常规类,并且创建注入很多对象实例

    • DataSourceRegister类是用来进行初始化数据源和并提供了执行动态切换数据源的工具类。

    DataSourceRegister注入主数据源和从数据源

    这里DataSourceRegister继承的EnvironmentAware接口,没有真正意义上去用它的用途,本身可以通过这个setEnvironment方法,进行注入Environment对象,从而可以读取其他的配置信息,目前主要用作一个hook方法。

    读取对应的环境变量

    public final void setEnvironment(Environment environment) {
            DruidEntity druidEntity = FileUtil.
                readYmlByClassPath("db_info", DruidEntity.class);
            defaultTargetDataSource =
                DataSourceUtil.createMainDataSource(druidEntity);
        }
    

    主要用于读取Druid的数据源模型信息。进行创建对应的数据源对象defaultTargetDataSource

    注入Bean到Spring容器中

    registerBeanDefinitions注册BeanDefinition对象模型

        public final void registerBeanDefinitions(AnnotationMetadata  annotationMetadata, BeanDefinitionRegistry  beanDefinitionRegistry) {
            // 0.将主数据源添加到数据源集合中
            DataSourceSet.putTargetDataSourcesMap(MAINDATASOURCE,defaultTargetDataSource);
            //1.创建DataSourceBean
            GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
            beanDefinition.setBeanClass(DataSource.class);
            beanDefinition.setSynthetic(true);
            MutablePropertyValues mpv = beanDefinition.getPropertyValues();
            //spring名称约定为defaultTargetDataSource和targetDataSources
            mpv.addPropertyValue("defaultTargetDataSource",defaultTargetDataSource);
            mpv.addPropertyValue("targetDataSources",DataSourceSet.getTargetDataSourcesMap());
            beanDefinitionRegistry.registerBeanDefinition("dataSource", beanDefinition);
        }
    

    完整的DataSourceRegister的案例

    public class DataSourceRegister<T> implements EnvironmentAware, ImportBeanDefinitionRegistrar {
    
        private javax.sql.DataSource defaultTargetDataSource;
    
        static final String MAINDATASOURCE = "mainDataSource";
    
        public final void setEnvironment(Environment environment) {
            DruidEntity druidEntity = FileUtil.
                readYmlByClassPath("db_info", DruidEntity.class);
            defaultTargetDataSource =
                DataSourceUtil.createMainDataSource(druidEntity);
        }
    
        public final void registerBeanDefinitions(AnnotationMetadata 
                                                  annotationMetadata, BeanDefinitionRegistry 
                                                  beanDefinitionRegistry) {
            // 0.将主数据源添加到数据源集合中
            DataSourceSet.putTargetDataSourcesMap(MAINDATASOURCE,
            defaultTargetDataSource);
            //1.创建DataSourceBean
            GenericBeanDefinition beanDefinition = new
                GenericBeanDefinition();
            beanDefinition.setBeanClass(DataSource.class);
            beanDefinition.setSynthetic(true);
            MutablePropertyValues mpv = beanDefinition.getPropertyValues();
            //spring名称约定为defaultTargetDataSource和targetDataSources
            mpv.addPropertyValue("defaultTargetDataSource",
            defaultTargetDataSource);
            mpv.addPropertyValue("targetDataSources",
            DataSourceSet.getTargetDataSourcesMap());
            beanDefinitionRegistry.registerBeanDefinition("dataSource", beanDefinition);
        }
    }
    

    执行流程解读

    • 动态数据源注册器类实现了ImportBeanDefinitionRegistrar接口,没错就是这个原因,由于实现了该接口让该类成为了拥有注册bean的能力。

    • 原理上也能说得通作为一个Bean的注册类是没有必要被注册为Spring容器的Bean对象

    • 虽然这样解释也不为过但我仍然想一探究竟,本来想大概找找spring涉及关键类如:ConfigurationClass,ConfigurationClassParser等,接下来我们需要看一下SpringBoot的总体加载流程。

    SpringApplication类的run方法

    SpringBoot启动时使用了SpringApplication类的run方法来牵引整个spring的初始化过程,源码如下。

    public ConfigurableApplicationContext run(String... args) {
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();
            ConfigurableApplicationContext context = null;
           // 错误原因分析器
            FailureAnalyzers analyzers = null;
            this.configureHeadlessProperty();
           // 重点分析:启动所有的运行的监听器
            SpringApplicationRunListeners listeners = this.getRunListeners(args);
            listeners.starting();
            try {
                // 解析ApplicationArgument数据
                ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
                // 通过监听器和传递的参数,实现相关的配置环境对象信息,预先
                // 进行配置Environment对象,设置全局环境变量容器
                ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
                // 输出相关的Banner控制,根据配置以及相关的Environment
                Banner printedBanner = this.printBanner(environment);
                // 创建Spring容器上下文。
                context = this.createApplicationContext();
                //创建和赋值错误解析器
                analyzers = new FailureAnalyzers(context);
                // 准备环境上下文进行配置相关的容器的上下文的参数。
                this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
                // 刷新上下文容器
                this.refreshContext(context);
                // 后置执行上下文操作
                this.afterRefresh(context, applicationArguments);
                // 完成监听器的后置完成处理操作
                listeners.finished(context, (Throwable)null);
                stopWatch.stop();
                if(this.logStartupInfo) {
                    (new StartupInfoLogger(this.mainApplicationClass)).
                        logStarted(this.getApplicationLog(), stopWatch);
                }
                return context;
            } catch (Throwable var9) {
                this.handleRunFailure(context, listeners, 
                                      (FailureAnalyzers)analyzers, 
                                      var9);
                throw new IllegalStateException(var9);
            }
        }
    

    根据上面的源码流程可以分析重点的加载过程

    1. 重点分析:启动所有的运行的监听器,获取SpringApplicationRunListeners; 从类路径下META-INF/spring.factories。回调所有的获取SpringApplicationRunListener.starting() 方法。
     SpringApplicationRunListeners listeners = this.getRunListeners(args);
     listeners.starting();
    
    1. 解析ApplicationArgument数据以及封装命令行参数,通过program参数的部分进行解析,并且加载到PropertiesSourcePlaceHolder中的环境变量容器内部。
    ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
    

    通过上面的监听器和传递的参数,实现相关的配置环境对象信息,预先进行配置Environment对象,设置全局环境变量容器

    1. 根据上面的全局变量容器进行配置以及初始化相关的Environment对象。
    ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
    
    1. 创建Spring容器上下文,在createApplicationContext当中,由于我们是web项目,则spring默认给我们创建了一个
    context = this.createApplicationContext();
    
    image

    AnnotationConfigEmbeddedWebApplicationContext,当然它也是继承GenericWebApplicationContext类和GenericApplicationContext类的,那么他默认会持有一个DefaultListableBeanFactory对象,这个对象可以用来创建Bean。

    1. 准备环境上下文进行配置相关的容器的上下文的参数
    this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
    
    1. 刷新上下文容器,refreshContext就是在做spring运行后的初始化工作。
    this.refreshContext(context);
    

    接着往下走,进入refreshContext中会调用一系列的refresh方法,最终进入AbstractApplicationContext中,主要将SpringBoot的容器对象数据和原本基础的Spring Framework的框架对象进行加载到容器中。

       @Override
        public void refresh() throws BeansException, IllegalStateException {
            synchronized (this.startupShutdownMonitor) {
                // Prepare this context for refreshing.
                prepareRefresh();
                // Tell the subclass to refresh the internal bean factory.
                ConfigurableListableBeanFactory beanFactory =
                    obtainFreshBeanFactory();
                // Prepare the bean factory for use in this context.
                prepareBeanFactory(beanFactory);
                try {
                    // Allows post-processing of the bean factory in context subclasses.
                    postProcessBeanFactory(beanFactory);
    
                    // Invoke factory processors registered as beans in the context.
                    invokeBeanFactoryPostProcessors(beanFactory);
    
                    // Register bean processors that intercept bean creation.
                    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();
                }
    
                catch (BeansException ex) {
                    if (logger.isWarnEnabled()) {
                        logger.warn("Exception encountered during context 
                                    initialization - " +"cancelling refresh attempt: " + ex);
                    }
    
                    // Destroy already created singletons to avoid dangling resources.
                    destroyBeans();
    
                    // Reset 'active' flag.
                    cancelRefresh(ex);
    
                    // Propagate exception to caller.
                    throw ex;
                }
                finally {
                    // Reset common introspection caches in Spring's core, since we
                    // might not ever need metadata for singleton beans anymore...
                    resetCommonCaches();
                }
            }
        }
    

    invokeBeanFactoryPostProcessors() 方法就是Bean在注册前期做的一系列数据收集工作,BeanDefinitionRegistry的容器注册BeanDefinition之前,调用相关的

    跟着堆栈继续深入,会进入到这个方法中,这个方法就是初始化bean前的所有轨迹:

    image

    在invokeBeanFactoryPostProcessors方法中继续跟进一系列方法就会看到在一开始的时候spring会初始化几个系统固有的Bean

    image

    继续调试后的关键点出现在这个方法中:

    public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
            List<BeanDefinitionHolder> configCandidates = new ArrayList<BeanDefinitionHolder>();
            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));
                }
            }
            // Return immediately if no @Configuration classes were found
            if (configCandidates.isEmpty()) {
                return;
            }
            // Sort by previously determined @Order value, if applicable
            Collections.sort(configCandidates, new 
                             Comparator<BeanDefinitionHolder>() {
                @Override
                public int compare(BeanDefinitionHolder bd1, 
                                   BeanDefinitionHolder bd2) {
                    int i1 = 
                        ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
                    int i2 = 
                        ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
                    return (i1 < i2) ? -1 : (i1 > i2) ? 1 : 0;
                }
            });
            // Detect any custom bean name generation strategy supplied through the enclosing application context
            SingletonBeanRegistry sbr = null;
            if (registry instanceof SingletonBeanRegistry) {
                sbr = (SingletonBeanRegistry) registry;
                if (!this.localBeanNameGeneratorSet && sbr.containsSingleton(CONFIGURATION_BEAN_NAME_GENERATOR)) {
                    BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
                    this.componentScanBeanNameGenerator = generator;
                    this.importBeanNameGenerator = generator;
                }
            }
            // Parse each @Configuration class
            ConfigurationClassParser parser = new ConfigurationClassParser(
                    this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry);
            Set<BeanDefinitionHolder> candidates = new LinkedHashSet<BeanDefinitionHolder>(configCandidates);
            Set<ConfigurationClass> alreadyParsed = new HashSet<ConfigurationClass>(configCandidates.size());
            do {
                parser.parse(candidates);
                parser.validate();
                Set<ConfigurationClass> configClasses = new LinkedHashSet<ConfigurationClass>(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();
                if (registry.getBeanDefinitionCount() > candidateNames.length) {
                    String[] newCandidateNames = registry.getBeanDefinitionNames();
                    Set<String> oldCandidateNames = new HashSet<String>(Arrays.asList(candidateNames));
                    Set<String> alreadyParsedClasses = new HashSet<String>();
                    for (ConfigurationClass configurationClass : alreadyParsed) {
                      alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
                    }
                    for (String candidateName : newCandidateNames) {
                        if (!oldCandidateNames.contains(candidateName)) {
                            BeanDefinition bd = registry.getBeanDefinition(candidateName);
                            if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
                                    !alreadyParsedClasses.contains(bd.getBeanClassName())) {
                                candidates.add(new BeanDefinitionHolder(bd, candidateName));
                            }
                        }
                    }
                    candidateNames = newCandidateNames;
                }
            }
            while (!candidates.isEmpty());
    
            // Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
            if (sbr != null) {
                if (!sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
                    sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
                }
            }
    
            if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
                ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
            }
        }
    

    而通过不断重复调试确定获得注册Bean的列表应该发生在配置的“剖析阶段”,也就是parser.parse(candidates);这个方法的内部,到了这里基本问题的答案已经要浮出水面了,我也不再粘贴无用的代码,如果你真的对这个问题比骄傲好奇可以自己跟踪并练习调试的源码技巧!

    当然在ConfigurationClassParser这个类中parse方法也是不少,只要静下心来逐渐分析,马上就能准确的找到Override的parse方法。

    protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
            if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
                return;
            }
    
            ConfigurationClass existingClass = this.configurationClasses.get(configClass);
            if (existingClass != null) {
                if (configClass.isImported()) {
                    if (existingClass.isImported()) {
                        existingClass.mergeImportedBy(configClass);
                    }
                    // Otherwise ignore new imported config class; existing non-imported class overrides it.
                    return;
                }
                else {
                    // Explicit bean definition found, probably replacing an import.
                    // Let's remove the old one and go with the new one.
                    this.configurationClasses.remove(configClass);
                    for (Iterator<ConfigurationClass> it = this.knownSuperclasses.values().iterator(); it.hasNext();) {
                        if (configClass.equals(it.next())) {
                            it.remove();
                        }
                    }
                }
            }
    
            // Recursively process the configuration class and its superclass hierarchy.
            SourceClass sourceClass = asSourceClass(configClass);
            do {
                sourceClass = doProcessConfigurationClass(configClass, sourceClass);//处理定义的配置类
            }
            while (sourceClass != null);
    
            this.configurationClasses.put(configClass, configClass);
        }
    
    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");
                }
            }
    
            // 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)) {
                for (AnnotationAttributes componentScan : componentScans) {
                    // The config class is annotated with @ComponentScan -> perform the scan immediately
                    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
                    for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
                        if (ConfigurationClassUtils.checkConfigurationClassCandidate(
                                holder.getBeanDefinition(), this.metadataReaderFactory)) {
                            parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
                        }
                    }
                }
            }
    
            // Process any @Import annotations
            processImports(configClass, sourceClass, getImports(sourceClass), true);//处理注解导入的类型
    
            // Process any @ImportResource annotations
            if (sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) {
                AnnotationAttributes importResource =
                        AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
                String[] resources = importResource.getStringArray("locations");
                Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
                for (String resource : resources) {
                    String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
                    configClass.addImportedResource(resolvedResource, readerClass);
                }
            }
    
            // Process individual @Bean methods
            Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
            for (MethodMetadata methodMetadata : beanMethods) {
                configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
            }
    
            // Process default methods on interfaces
            processInterfaces(configClass, sourceClass);
    
            // Process superclass, if any
            if (sourceClass.getMetadata().hasSuperClass()) {
                String superclass = sourceClass.getMetadata().getSuperClassName();
                if (!superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) {
                    this.knownSuperclasses.put(superclass, configClass);
                    // Superclass found, return its annotation metadata and recurse
                    return sourceClass.getSuperClass();
                }
            }
    
            // No superclass -> processing is complete
            return null;
        }
    

    以上两个方法中标红的就是关键点。而且spring的大师们也把注释写的十分明显:”//Process any @Import annotations“,到这里已经彻底豁然开朗!

    spring会先去处理scan,将你程序内部的所有要注册的Bean全部获得(自然包括那些configuration),这里统称为ConfigurationClass,scan全部整理完毕后才会去处理@Import注解时导入的类!

    我们回到最初的问题 DataSourceRegister和A两个类为什么A成为了Bean但DataSourceRegister却未成为Bean呢?

    在processImports方法中,很明显candidate.isAssignable(ImportBeanDefinitionRegistrar.class)时操作为:configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());而普通的类通过processConfigurationClass(candidate.asConfigClass(configClass));方法,最终会被放在ConfigurationClassParser类的成员变量configurationClasses中,最终被初始化为Bean。

    private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
                Collection<SourceClass> importCandidates, boolean checkForCircularImports) throws IOException {
    
            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) {
                        if (candidate.isAssignable(ImportSelector.class)) {
                            // Candidate class is an ImportSelector -> delegate to it to determine imports
                            Class<?> candidateClass = candidate.loadClass();
                            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 {
                                String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                                Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
                                processImports(configClass, currentSourceClass, importSourceClasses, false);
                            }
                        }
                        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);
                            configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
                        }
                        else {
                            // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
                            // process it as an @Configuration class
                            this.importStack.registerImport(
                                    currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
                            processConfigurationClass(candidate.asConfigClass(configClass));
                        }
                    }
                }
                catch (BeanDefinitionStoreException ex) {
                    throw ex;
                }
                catch (Throwable ex) {
                    throw new BeanDefinitionStoreException(
                            "Failed to process import candidates for configuration class [" +
                            configClass.getMetadata().getClassName() + "]", ex);
                }
                finally {
                    this.importStack.pop();
                }
            }
        }
    

    后置执行上下文操作

    this.afterRefresh(context, applicationArguments);
    

    完成监听器的后置完成处理操作

    listeners.finished(context, (Throwable)null);
    

    至此,总体的mportBeanDefinitionRegistrar的对象注入体系就基本介绍完了

    相关文章

      网友评论

        本文标题:【深入浅出Spring原理及实战】「源码调试分析」结合DataS

        本文链接:https://www.haomeiwen.com/subject/gekacdtx.html