Spring Boot源码之旅七SpringApplication启动流程七

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬

学习必须往深处挖,挖的越深,基础越扎实!

阶段1、深入多线程

阶段2、深入多线程设计模式

阶段3、深入juc源码解析


阶段4、深入jdk其余源码解析


阶段5、深入jvm源码解析

 

码哥源码部分

码哥讲源码-原理源码篇【2024年最新大厂关于线程池使用的场景题】

码哥讲源码【炸雷啦!炸雷啦!黄光头他终于跑路啦!】

码哥讲源码-【jvm课程前置知识及c/c++调试环境搭建】

 

​​​​​​码哥讲源码-原理源码篇【揭秘join方法的唤醒本质上决定于jvm的底层析构函数】

码哥源码-原理源码篇【Doug Lea为什么要将成员变量赋值给局部变量后再操作?】

码哥讲源码【你水不是你的错,但是你胡说八道就是你不对了!】

码哥讲源码【谁再说Spring不支持多线程事务,你给我抽他!】

终结B站没人能讲清楚红黑树的历史,不服等你来踢馆!

打脸系列【020-3小时讲解MESI协议和volatile之间的关系,那些将x86下的验证结果当作最终结果的水货们请闭嘴】

 

初始化基本流程

SpringApplication的prepareContext准备上下文

这里面有干了很多事,他会将最前面获得的初始化器都初始化,然后广播上下文准备好事件,然后这里居然还设置了不能覆盖同名bean定义,这样就避免了用户去捣乱了。最后会把启动类的注册到bean定义里,然后广播上下文加载完成事件。

    private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
    			SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
    		context.setEnvironment(environment);//配置环境
    		postProcessApplicationContext(context);//一些设置处理
    		applyInitializers(context);//初始化监听器进行初始化
    		listeners.contextPrepared(context);//广播上下文准备好的事件ApplicationContextInitializedEvent
    		if (this.logStartupInfo) {//控制台打启动信息
    			logStartupInfo(context.getParent() == null);
    			logStartupProfileInfo(context);
    		}
    		// Add boot specific singleton beans
    		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    		beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
    		if (printedBanner != null) {
    			beanFactory.registerSingleton("springBootBanner", printedBanner);
    		}
    		if (beanFactory instanceof DefaultListableBeanFactory) {
    			((DefaultListableBeanFactory) beanFactory)//不允许同名的bean定义的覆盖
    					.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
    		}
    		if (this.lazyInitialization) {
    			context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
    		}
    		// Load the sources
    		Set<Object> sources = getAllSources();//获取启动源集合,就是传给SpringApplication的参数类
    		Assert.notEmpty(sources, "Sources must not be empty");
    		load(context, sources.toArray(new Object[0]));//注册启动类的bean定义
    		listeners.contextLoaded(context);//广播上下文加载完成事件ApplicationPreparedEvent
    	}

postProcessApplicationContext处理

就是提前去注册bean名字生成器,资源加载器,还有前面创建的转换器也要放进来。

    	protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
    		if (this.beanNameGenerator != null) {
    			context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
    					this.beanNameGenerator);
    		}
    		if (this.resourceLoader != null) {
    			if (context instanceof GenericApplicationContext) {
    				((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);
    			}
    			if (context instanceof DefaultResourceLoader) {
    				((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());
    			}
    		}
    		if (this.addConversionService) {//添加转换器
    			context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
    		}
    	}

applyInitializers初始化器初始化

获取最开始创建的初始化器,遍历每一个初始化器,进行初始化。

    	protected void applyInitializers(ConfigurableApplicationContext context) {
    		for (ApplicationContextInitializer initializer : getInitializers()) {
    			Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
    					ApplicationContextInitializer.class);//获取ApplicationContextInitializer接口的泛型类型
    			Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");//context不是requiredType类型是不行的
    			initializer.initialize(context);//初始化
    		}
    	}

load

创建bean定义加载器,进行bean定义的加载,就是把sources注册到bean定义里。

    protected void load(ApplicationContext context, Object[] sources) {
    		if (logger.isDebugEnabled()) {
    			logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
    		}
    		BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
    		if (this.beanNameGenerator != null) {
    			loader.setBeanNameGenerator(this.beanNameGenerator);
    		}
    		if (this.resourceLoader != null) {
    			loader.setResourceLoader(this.resourceLoader);
    		}
    		if (this.environment != null) {
    			loader.setEnvironment(this.environment);
    		}
    		loader.load();
    	}
    
    	//遍历每一个加载
    	int load() {
    		int count = 0;
    		for (Object source : this.sources) {
    			count += load(source);
    		}
    		return count;
    	}

根据不同类型加载,最后都是registerBean

SpringApplication的refreshContext刷新上下文

除了刷新外,还注册了一个钩子,

    	private void refreshContext(ConfigurableApplicationContext context) {
    		refresh(context);
    		if (this.registerShutdownHook) {
    			try {
    				context.registerShutdownHook();
    			}
    			catch (AccessControlException ex) {
    				// Not allowed in some environments.
    			}
    		}
    	}

refresh

调用当前上下文AbstractApplicationContext类型的refresh,当前上下文是ServletWebServerApplicationContext类型的,所以会调用到他的refresh

    	protected void refresh(ApplicationContext applicationContext) {
    		Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
    		((AbstractApplicationContext) applicationContext).refresh();
    	}

ServletWebServerApplicationContext的refresh

然后他又调用父类的refresh

    	@Override
    	public final void refresh() throws BeansException, IllegalStateException {
    		try {
    			super.refresh();
    		}
    		catch (RuntimeException ex) {
    			stopAndReleaseWebServer();
    			throw ex;
    		}
    	}

里面就是springrefresh方法,进行初始化,就不讲了,可以看我写的spring源码文章,其实内部干了不少事情,后面会讲,毕竟前面那么多初始化器初始化了,肯定会对后面spring初始化有所作用。
看看钩子方法,其实就是注册一个关闭线程:


刷新完成后基本没啥事了,就进行启动完成事件通知,还有一些的ApplicationRunnerCommandLineRunner类型的bean要处理,一般是没有的:

最后再通知一个运行事件初始化就完成了:

初始化基本讲完了,但是还有好多细节没讲,比如我们的tomcat怎么启动的呀,哪些自动配置怎么回事,我们下次讲啦。

  • 21
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值