首页 > 其他分享 >IOC 启动流程

IOC 启动流程

时间:2024-10-25 12:59:38浏览次数:5  
标签:BeanFactory 流程 beanFactory bean 启动 context new IOC class

初始化12步骤

容器创建会进入 refresh 方法,总共 12 个步骤

// org.springframework.context.support.AbstractApplicationContext#refresh
@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

        // 步骤一:准备阶段
        prepareRefresh();

        // 步骤二:获得一个 BeanFactory(bean 定义信息在这一步会保存好)
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // 步骤三:给 BeanFactory 赋能
        prepareBeanFactory(beanFactory);

        try {
            // 步骤四:扩展 BeanFactory(这是个钩子方法,目的是留给子类的,子类可根据需要再次赋予 BeanFactory 更多能力)
            postProcessBeanFactory(beanFactory);

            StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
            
            // 步骤五:执行 BeanFactoryProPercessor
            invokeBeanFactoryPostProcessors(beanFactory);

            // Register bean processors that intercept bean creation.
            registerBeanPostProcessors(beanFactory);
            beanPostProcess.end();

            // 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();
        }
		...
    }
}

步骤1:准备容器

// org.springframework.context.support.AbstractApplicationContext#prepareRefresh
protected void prepareRefresh() {
    // 计时
    this.startupDate = System.currentTimeMillis();
    this.closed.set(false);
    this.active.set(true);

    // 这是一个模板方法,子类复写自定义配置 spring 信息,MVC、Boot、Cloud 等很多框架都复写过
    initPropertySources();

    // 必要参数验证
    getEnvironment().validateRequiredProperties();

    // 初始化事件监听器和事件,都置为空列表
    if (this.earlyApplicationListeners == null) {
        this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
    }
    else {
        this.applicationListeners.clear();
        this.applicationListeners.addAll(this.earlyApplicationListeners);
    }
    this.earlyApplicationEvents = new LinkedHashSet<>();
}

步骤2:获得 BeanFactory

  • 产生一个新鲜的 BeanFactory
  • 保存好 bean 定义信息

ApplicationContext 一般叫容器,BeanFactory 是真正保存 bean 的地方

// org.springframework.context.support.AbstractApplicationContext#obtainFreshBeanFactory
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    refreshBeanFactory();
    return getBeanFactory();
}

// org.springframework.context.support.AbstractRefreshableApplicationContext#refreshBeanFactory
@Override
protected final void refreshBeanFactory() throws BeansException {
    if (hasBeanFactory()) {
        destroyBeans();
        closeBeanFactory();
    }
    try {
        DefaultListableBeanFactory beanFactory = createBeanFactory();
        beanFactory.setSerializationId(getId());
        customizeBeanFactory(beanFactory);
        // 这里会把 bean 定义信息准备好
        loadBeanDefinitions(beanFactory);
        this.beanFactory = beanFactory;
    }
    catch (IOException ex) {
        throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
    }
}

步骤3:初始化 BeanFactory

上一步刚初始化了 BeanFactory,还没什么能力,这一步是配置 BeanFactory 的属性,让其具有处理某些动作的能力

当遇到相关动作,就会使用这里赋予的能力,所以这里代码比较简单,就是给不同属性赋值

  • 比如 addBeanPostProcessor 给 BeanFactory 添加了一些后置处理器
    • 加入到 beanPostProcessors 属性中
    • 比如 SPEL 解析器,各种 Aware 回调处理器
  • 比如 ignoreDependencyInterface 忽略了一些自动装配的类
    • 加入到 ignoredDependencyInterfaces 属性中
    • 1,这些接口的实现类的属性@Autowired 不生效(不能注入别的 bean)
    • 2,别的 bean 里也使用 @Autowired 给属性赋值也不生效(不能被别的 bean 注入)
  • 比如 registerResolvableDependency 提前放入一些可能需要别的依赖的 bean
    • 是加入到 resolvableDependencies 属性中
    • 这一步是为了提升效率的,如果不提前放,后面找到这些 bean 也会放进去(事先就放进去)
// org.springframework.context.support.AbstractApplicationContext#prepareBeanFactory
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    // 类加载器
    beanFactory.setBeanClassLoader(getClassLoader());
    
    // 是否不解析 SPEL,spring.spel.ignore 配置项来控制
    if (!shouldIgnoreSpel) { 
        // StandardBeanExpressionResolver:SPEL 表达式解析器
        beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
    }
    
    // ResourceEditorRegistrar:用于把配置文件转成一个 Resource 对象
    beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

    // 添加后置处理器,如果有 bean 实现了 ApplicationContextAware,BeanFactory 使用 ApplicationContextAwareProcessor 去处理
    beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
    
    // 忽略一些自动装配
    beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
    beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
    beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
    beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
    beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
    beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
    beanFactory.ignoreDependencyInterface(ApplicationStartupAware.class);

    // 这些注册为 bean,比如可以直接 @Autorired BeanFactory beanFactory 来注入 BeanFactory
    beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
    beanFactory.registerResolvableDependency(ResourceLoader.class, this);
    beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
    beanFactory.registerResolvableDependency(ApplicationContext.class, this);

    // 添加后置处理器,让 BeanFactory 可以处理时间发布与监听
    beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

    // GraalVM 支持,java 虚拟机一般都是 hotspot VM
    if (!NativeDetector.inNativeImage() && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
        beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
        beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    }

    // 再实现注册一些默认的 bean
    if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
        beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
    }
    if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
        beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
    }
    if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
        beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
    }
    if (!beanFactory.containsLocalBean(APPLICATION_STARTUP_BEAN_NAME)) {
        beanFactory.registerSingleton(APPLICATION_STARTUP_BEAN_NAME, getApplicationStartup());
    }
}

步骤4:扩展 BeanFactory

上一步已经赋能了,这里再次赋能,这是一个钩子方法,没有实现,所以专门提供给子类的

比如 SpringMVC 扩展后我们标注 @Controller 就会把这个 bean 注入到容器中

步骤5:执行 BeanFactoryPostProcesser

步骤 3 给 BeanFactory 放入了一批后置处理器,这一步就是要执行他们

// org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    // this.getBeanFactoryPostProcessors() 就是获取所有 bean 工厂后置处理器,此时是空的 invokeBeanFactoryPostProcessors 会去找
    // invokeBeanFactoryPostProcessors 首先找到所有实现了 BeanFactoryPostProcessor 接口的 bean,然后挨个调用其 postProcessBeanFactory 方法
    // BeanFactoryPostProcessor 干嘛的?修改 bean 定义的,在创建前对 bean 定义做一些修改,后续根据修改后的定义来创建 bean
    PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, this.getBeanFactoryPostProcessors());
    // GraalVM 支持
    if (!NativeDetector.inNativeImage() && beanFactory.getTempClassLoader() == null && beanFactory.containsBean("loadTimeWeaver")) {
        beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
        beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    }
}

// org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors(org.springframework.beans.factory.config.ConfigurableListableBeanFactory, java.util.List<org.springframework.beans.factory.config.BeanFactoryPostProcessor>)
public static void invokeBeanFactoryPostProcessors(
        ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
    ...
    List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
    List<String> orderedPostProcessorNames = new ArrayList<>();
    List<String> nonOrderedPostProcessorNames = new ArrayList<>();
    for (String ppName : postProcessorNames) {
        if (processedBeans.contains(ppName)) {
            // skip - already processed in first phase above
        }
        else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
            // 找到所有实现了 BeanFactoryPostProcessor 的类
            priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
        }
        else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
            orderedPostProcessorNames.add(ppName);
        }
        else {
            nonOrderedPostProcessorNames.add(ppName);
        }
    }
    ...
}

标签:BeanFactory,流程,beanFactory,bean,启动,context,new,IOC,class
From: https://www.cnblogs.com/cyrushuang/p/18502280

相关文章

  • Linux基础——虚机mysql库覆盖/usr/lib64/libcrypto.so.1.1.1f无法启动
    1、问题描述租户新增数据库mysql,手动覆盖/usr/lib64中的libcrypto.so.1.1.1f库文件,导致主机重启进入救援模式。 2、问题分析i.发现报错poweroff:errorwhileloadingsharedlibraries:libcrypto.so.1.1:cannotopensharedobjectfile:Nosuchfileordirectoryii.检......
  • 虚幻引擎实操(1)《原神,启动!》
    介绍 这个文章会带大家用虚幻引擎粗略的实现原()神中的一些功能,当然啦不是做一个完整的原神出来,主要面向对象为没有编程基础或者刚刚入门的同学前言好像对于大部分0基础的同学来说虚幻引擎的学习比较抽象(做游戏本身就挺抽象的,没有狠活挣不出来qwq)。比如虚幻的界面,数百个接口,......
  • Windows 11 查看已连接 WiFi 的全流程
    Windows11查看已连接WiFi的全流程以下是通过命令行查看已连接WiFi信息的完整操作流程。1.打开命令提示符(CommandPrompt)按Win+S,在搜索框中输入cmd,点击“命令提示符”以管理员身份运行。2.查看已连接的WiFi网络信息在命令提示符中输入以下命令,按下回......
  • [Flink SQL] FlinkCdcSqlJob启动时因MYSQL serverTimeZone而报错:`The MySQL server ha
    1问题描述FlinkCdcSqlJob启动时报错...Causedby:org.apache.flink.table.api.ValidationException:TheMySQLserverhasatimezoneoffset(0secondsaheadofUTC)whichdoesnotmatchtheconfiguredtimezoneAsia/Shanghai.Specifytherightserver-time-z......
  • java启动命令详解
    java-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8002-server-Xms4g-Xmx6g-XX:+HeapDumpOnOutOfMemoryError-XX:HeapDumpPath=/data/data/dump.bin-Dfile.encoding=UTF-8-Djava.awt.headless=true-XX:+UseG1GC-XX:MaxGCPauseMillis=200-XX:G1H......
  • Android 10.0 截屏流程
    通常未通过特殊定制的Android系统,截屏都是经过同时按住音量下键和电源键来截屏。本篇文章就只讨论使用这些特殊按键来进行截屏。这里我们就要明白事件是在哪里进行分发拦截的。通过源码的分析,我们发现是在PhoneWindowManager.java中。PhoneWindowManager#interceptKeyB......
  • 安装node及vue项目的启动
    1、ubuntu安装npmsudoaptinstallnodejsnpm2、设置包下载源npmconfigsetregistryhttps://registry.npmmirror.com/3.安装包及运行npminstall安装成功后会生成一个node_moudels目录运行:npmrunserve4、常见报错及解决方式(1)oldlockfile报错npmWARNoldlo......
  • Python 文件与模块的运行顺序及调用时的执行流程详解【大白话版本!!】
    Python文件与模块的运行顺序及调用执行流程详解引言ython是一种强大的编程语言,具有极大的灵活性和简洁性。无论是在开发小型脚本,还是构建复杂的应用程序时,理解Python文件的运行顺序以及模块调用时的执行流程都至关重要。尤其当你开发大规模项目,涉及到多个模块(文件)之间......
  • 《苍翼混沌效应》游戏启动提示bdlogmgr.dll文件丢失?简单几步轻松修复
    当您在启动《苍翼混沌效应》(BlazBlue:ChaosEffect)或任何其他游戏时遇到提示丢失bdlogmgr.dll文件的问题,通常这意味着您的游戏缺少了某个重要的动态链接库(DLL)文件。以下是一些可能的解决方法:重新安装游戏尝试卸载游戏,然后从官方渠道重新下载并安装最新版本的游戏。这可以确......
  • mysql无法成功启动服务怎么办
    在遇到MySQL无法成功启动服务的情况下,应采取的步骤和解决方法:1.检查错误日志;2.检查端口冲突;3.检查配置文件;4.检查磁盘空间;5.检查文件权限;6.数据库修复;7.使用日志和监控工具;8.寻求专业帮助,以确保数据库系统能够正常运行。MySQL是许多应用程序和网站的核心数据库管理系统之一。1......