十、SpringBoot启动-刷新应用上下文前的准备

1.刷新应用上下文的准备

this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
	1.上文初始化了context,这里说的准备工作就相当于给context里面的属性进行设置;
	2.完成一些bean对象的创建;
我们自己写的核心启动类bean对象的创建也在prepareContext这个方法里完成。

在这里插入图片描述

1.1.设置应用上下的属性

1.1.1.设置environment

context.setEnvironment(environment);
设置容器环境

1.1.2.postProcessApplicationContext

postProcessApplicationContext(context);
执行容器后置处理

在这里插入图片描述

上一篇文章说到,在初始化应用上下文时调用BeanUtils.instantiateClass(contextClass),就已经创建了IOC容器对象;
context.getBeanFactory()就是获取容器对象,那么setConversionService就是给IOC容器设置转换服务(转换器);
转换器:比如我们在容器中可以把一些整数的字符串转换为整形。

1.1.3.applyInitializers

applyInitializers(context);
应用初始化器。

在这里插入图片描述····getInitializers()获取到初始化器(执行run方法钱准备阶段,在SpringApplication的构造方法里,准备的初始化器),遍历启动执行(initializer.initialize(context))。

1.1.4.listeners.contextPrepared(context);

向各个监听器发送容器已经准备好的事件

在这里插入图片描述····遍历监听器,将context发送给监听器。

完成了前三步,应用上下文的设置基本完成了,就发送给各个监听器。
这些监听器是SpringApplicationRunListeners类型的,根据之前的分析可以知道,
这个listeners只有一个值,就是EventPublishingRunListener(启动监听器)。

1.2.创建需要的bean对象

ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();

1.2.1.封装args

beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
这个applicationArguments,就是我们传入的main方法的args(命令行参数)

在这里插入图片描述····将main方法的args参数封装成单例bean,注册进IOC容器。

1.2.2.封装banner

如果printedBanner不为空,那么也会被封装成单例bean;
这里我没有设置banner.txt,所以为null。

在这里插入图片描述

1.2.3.设置允许bean的定义信息被覆盖

判断这个beanFactory是不是DefaultListableBeanFactory类型;
如果是,就设置允许bean的定义信息被覆盖;
显然,之前已经知道这个IOC(beanFactory)容器就是DefaultListableBeanFactory类型的。

在这里插入图片描述

1.2.4.判断当前是否是懒加载

如果是就设置LazyInitializationBeanFactoryPostProcessor

在这里插入图片描述

1.3.获取资源并加载启动类

1.3.1.获取source

Set<Object> sources = this.getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");

在这里插入图片描述····source是我们的启动类

1.3.2.将启动类注入容器

this.load(context, sources.toArray(new Object[0]));
sources是一个集合,取出我们的启动类。
load方法,通过核心启动类的字节码,生成实例对象,设置到IOC容器中。

在这里插入图片描述在这里插入图片描述

  • createBeanDefinitionLoader:方法两个参数;
  • this.getBeanDefinitionRegistry(context);
  • sources;

在这里插入图片描述所以将context向上造型成BeanDefinitionRegistry;

Spring容器在启动的时候,会先将类解析成BeanDefinition对象
然后将BeanDefinition存到DefaultListableBeanFactory里面的一个map中

在这里插入图片描述再看看这个BeanDefinitionLoader的构造方法
在这里插入图片描述

  • annotatedReader:注解形式的Bean定义读取器(Component等)。
  • xmlReader:xml配置形式的Bean定义读取器;
  • scanner:类路径扫描Bean定义读取器;
  • addExcludeFilter:扫描器添加排除过滤器。

经过前面步骤,loader(BeanDefinitionLoader)对象被创建出来了。
loader再调用load方法
在这里插入图片描述
又调用load方法在这里插入图片描述再调用load方法
在这里插入图片描述

isComponent判断source启动类上面有没有Component注解;
很明显是有,那么就使用注解形式的bean定义读取器,将启动类的BeanDefinition注册进beanDefinitionMap。

在这里插入图片描述
这个componentClasses是传入的启动类resource。

在这里插入图片描述在这个doRegisterBean方法里面,创建并设置AnnotatedGenericBeanDefinition的属性,当然是启动类相关的。

private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name, @Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier, @Nullable BeanDefinitionCustomizer[] customizers) {
        AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
        if (!this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
            abd.setInstanceSupplier(supplier);
            ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
            abd.setScope(scopeMetadata.getScopeName());
            String beanName = name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry);
            AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
            int var10;
            int var11;
            if (qualifiers != null) {
                Class[] var9 = qualifiers;
                var10 = qualifiers.length;

                for(var11 = 0; var11 < var10; ++var11) {
                    Class<? extends Annotation> qualifier = var9[var11];
                    if (Primary.class == qualifier) {
                        abd.setPrimary(true);
                    } else if (Lazy.class == qualifier) {
                        abd.setLazyInit(true);
                    } else {
                        abd.addQualifier(new AutowireCandidateQualifier(qualifier));
                    }
                }
            }

            if (customizers != null) {
                BeanDefinitionCustomizer[] var13 = customizers;
                var10 = customizers.length;

                for(var11 = 0; var11 < var10; ++var11) {
                    BeanDefinitionCustomizer customizer = var13[var11];
                    customizer.customize(abd);
                }
            }

            BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
            definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
            BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
        }
    }

····设置这个beanDefinition的scop和beanName,然后通过AnnotatedGenericBeanDefinition和beanName构建一个beandefinition的持有对象。
····registerBeanDefinition看下图:
在这里插入图片描述····这里又调用registerBeanDefinition方法,这里调用的具体实现时DefaulListableBeanDefinition的实现,传递的参数时beanName和BeanDefinition

在这里插入图片描述在这里插入图片描述····在DefaultListableBeanFactory的registerBeanDefinition方法中,将beanName和beanDefinition分别作为key、value添加到beanDefinitionMap中。

核心启动类存入了beanDefinitionMap,并且在之前将这个beanDefinition设置成了singleton;
那么之后会从beanDefinitionMap中取出beanDefinition;
beanDefinition里面又存有启动类的Class字节码;
通过反射可以实例出对象,然后存到DefaultListableBeanFactory的单例池中;
单例池就是这个ConcurrentHashMap,这个singletonBeanNamesByType。

在这里插入图片描述

1.4.发布容器加载事件

启动类添加到了beanDefinitionMap;
那么刷新应用上下文的准备工作就只剩最后一步;
listeners.contextLoaded(context);

1.5.观察context

刷新应用上下文前的准备工作做完之后,我们看看context的属性值

在这里插入图片描述在这里插入图片描述

····其实从这里也可以看出来,启动类只是添加到了beanDefinitionMap里面,还没有生成实例注入到单例池里面。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Spring Boot启动流程大致如下: 1. 创建 SpringApplication 对象:SpringApplication 是 Spring Boot 的核心类,负责启动应用程序并装载 Spring 上下文。在创建 SpringApplication 对象时,会传入一个主配置类,用于指定 Spring Boot 应用的入口配置。 2. 加载应用配置:SpringApplication 会读取应用的配置文件(如 application.properties 或 application.yml),并将配置信息加载到 Spring 环境中。 3. 创建 Spring 环境:SpringApplication 根据配置文件中的属性,创建一个新的 Spring 环境对象(Environment),该对象负责管理配置属性和激活的 profile。 4. 准备应用上下文SpringApplication 根据配置创建一个空的应用上下文(ApplicationContext)对象。 5. 预处理上下文:该步骤涉及许多上下文初始化的准备工作,例如设置环境、类加载器、属性源等。 6. 加载应用上下文:使用 Spring 的装载机制(Loaders)加载一系列 BeanDefinition 到应用上下文中。 7. 刷新应用上下文应用上下文准备就绪后,调用 refresh() 方法进行刷新。在这个过程中,Spring启动各种事件、监听器等机制,并完成 Bean 的实例化、依赖注入等操作。 8. 启动应用刷新完成后,调用 SpringApplication 的 run() 方法启动应用。该方法会执行一些准备工作,然后调用 Tomcat 或其他嵌入式容器来启动 Web 服务。 9. 应用运行:应用运行起来后,会开始处理请求,并将结果返回给客户端。同时,Spring Boot 提供了许多自动配置和约定大于配置的功能,简化了开发过程。 总结起来,Spring Boot启动流程可以分为初始化环境、加载配置、创建上下文刷新上下文启动应用等阶段。这些步骤使得开发者可以快速构建和运行 Spring Boot 应用程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值