springBoot启动流程运行流程代码逐行详解

运行流程详细代码

public ConfigurableApplicationContext run(String... args) {
        //初始化计时器
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        //初始化异常报告的集合
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
        //设置默认属性(不重要)
        this.configureHeadlessProperty();
        //下面两根方法是重点方法
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        //上面的listeners从spring.properties加载出来只有一个EventPublishingRunListener
        listeners.starting();

        Collection exceptionReporters;
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            //构造容器环境
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            //设置需要忽略的bean spring.Beaninfo.ignore
            this.configureIgnoreBeanInfo(environment);
            //打印banner
            Banner printedBanner = this.printBanner(environment);
            //创建容器
            context = this.createApplicationContext();
            //实例化SpringBootExceptionReporter.class,用来支持报告关于启动的错误
            exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
            //准备容器
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            //刷新容器
            this.refreshContext(context);
            //刷新容器后的扩展接口
            this.afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }

            listeners.started(context);
            this.callRunners(context, applicationArguments);
        } catch (Throwable var10) {
            this.handleRunFailure(context, var10, exceptionReporters, listeners);
            throw new IllegalStateException(var10);
        }

        try {
            listeners.running(context);
            return context;
        } catch (Throwable var9) {
            this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var9);
        }
    }

阶段一:回调支持应用启动的listenters

1,getRunListeners()方法详解

private SpringApplicationRunListeners getRunListeners(String[] args) {
		Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
		//getSpringFactoriesInstances:从spring.factory中获取key为SpringApplicationRunListener的key-value对,并对value进行实例化
		return new SpringApplicationRunListeners(logger,
				getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
	}

上面的getSpringFactoriesInstances会实例化下面这个类:

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {

	private final SpringApplication application;

	private final String[] args;

	private final SimpleApplicationEventMulticaster initialMulticaster;
	//通过构造方法实例化该类
	public EventPublishingRunListener(SpringApplication application, String[] args) {
	    //传入之前构造流程中的生成的:SpringApplication对象
		this.application = application;
		this.args = args;
		this.initialMulticaster = new SimpleApplicationEventMulticaster();
		//将之前加载的11个listeners放入到initialMulticaster缓存中
		for (ApplicationListener<?> listener : application.getListeners()) {
		//initialMulticaster实际上是个set集合
			this.initialMulticaster.addApplicationListener(listener);
		}
	}
	

starting()方法详解

由于扫描spring.properties加载出来只有一个EventPublishingRunListener
所有下面会走EventPublishingRunListener的starting方法

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
................

	@Override
	public int getOrder() {
		return 0;
	}

//在上一个步骤中,已经将构造阶段初始化的11个listener保存到initialMulticaster的set集合中了
//新建ApplicationStartingEvent应用的启动事件
	@Override
	public void starting() {
		this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
	}
...........

multicastEvent()方法详解

//ApplicationEvent:上一步新建的对象,保存了构造阶段的SpringApplication对象
//这个方法的功能是:从构造阶段中获取到11个listenes中招到支持应用启动事件的4个listeners,并逐个做回调,
    public void multicastEvent(ApplicationEvent event) {
        this.multicastEvent(event, this.resolveDefaultEventType(event));
    }

    public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
        ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
        Executor executor = this.getTaskExecutor();
        //获取支持应用启动事件的的listeners,经过这个方法之后,会从构建阶段中的11个listeners中获取到4个listeners
        Iterator var5 = this.getApplicationListeners(event, type).iterator(); 

        while(var5.hasNext()) {
            ApplicationListener<?> listener = (ApplicationListener)var5.next();
            if (executor != null) {
                executor.execute(() -> {
                    this.invokeListener(listener, event);
                });
            } else {
                this.invokeListener(listener, event);
            }
        }

    }

在这里插入图片描述

阶段二:准备环境,回调在构建阶段涉及到环境准备的listeners,并完成环境变量的加载

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// Create and configure the environment
		//获取或者创建一个环境,获取一些系统的环境变量和属性,例如:javahome,os标识等等
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		//配置一些信息,比如支持命令行的属性信息(java调优中会在启动的命令行中添加一些调优命令)
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		//下面这个方法不重要
		ConfigurationPropertySources.attach(environment);
		//从之前构造函数中拿到的11个listeners里面找到响应环境准备好之后的listeners事件,并触发对这个事件的处理
		listeners.environmentPrepared(environment);
		bindToSpringApplication(environment);
		//截止到这里,所有涉及到环境准备的listeners已经回调完毕
		if (!this.isCustomEnvironment) {
			environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
					deduceEnvironmentClass());
		}
		ConfigurationPropertySources.attach(environment);
		//环境准备完毕
		return environment;
	}

getOrCreateEnvironment()接口详解:

private ConfigurableEnvironment getOrCreateEnvironment() {
        //根据环境的不同,实例化不同的环境
		if (this.environment != null) {
			return this.environment;
		}
		switch (this.webApplicationType) {
		case SERVLET:
			return new StandardServletEnvironment();
		case REACTIVE:
			return new StandardReactiveWebEnvironment();
		default:
			return new StandardEnvironment();
		}
	}

上面方法会一层层调用父类的构造方法,直到调用到下面这个类的构造方法

public class StandardServletEnvironment extends StandardEnvironment implements ConfigurableWebEnvironment {
    public static final String SERVLET_CONTEXT_PROPERTY_SOURCE_NAME = "servletContextInitParams";
    public static final String SERVLET_CONFIG_PROPERTY_SOURCE_NAME = "servletConfigInitParams";
    public static final String JNDI_PROPERTY_SOURCE_NAME = "jndiProperties";

    public StandardServletEnvironment() {
    }

    protected void customizePropertySources(MutablePropertySources propertySources) {
        propertySources.addLast(new StubPropertySource("servletConfigInitParams"));
        propertySources.addLast(new StubPropertySource("servletContextInitParams"));
        if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
            propertySources.addLast(new JndiPropertySource("jndiProperties"));
        }

        super.customizePropertySources(propertySources);
    }

下面重点就是这个方法:customizePropertySources()

  protected void customizePropertySources(MutablePropertySources propertySources) {
        //获取系统的一些属性信息,比如虚拟机版本,os标识等等
        propertySources.addLast(new PropertiesPropertySource("systemProperties", this.getSystemProperties()));
        //获取系统的一些环境变量:java_home.....
        propertySources.addLast(new SystemEnvironmentPropertySource("systemEnvironment", this.getSystemEnvironment()));
    }

在这里插入图片描述

阶段三:创建容器(类似于spring的ioc容器)

这块代码比较复杂,总结来讲就是:

  1. 基本属性赋值,重点是beanFactory、reader、scanner和environment这四个属性
  2. reader的创建过程中给beanFactory中的beanDefinitionMap、beanDefintionNames这两个属性设置5个值,并且自己的registry属性引用了应用上下文对象,scanner的registry也引用了应用上下文对象
  3. 应用上下文中的environment是创建应用上下文过程中新创建的环境,并不是我们之前run方法创建好的环境。
	//根据webApplicationType推断并实例化 ioc容器类型,此处是实例化的SERVLET容器
	protected ConfigurableApplicationContext createApplicationContext() {
		Class<?> contextClass = this.applicationContextClass;
		if (contextClass == null) {
			try {
				switch (this.webApplicationType) {
				case SERVLET:
					contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
					break;
				case REACTIVE:
					contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
					break;
				default:
					contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
				}
			}
			catch (ClassNotFoundException ex) {
				throw new IllegalStateException(
						"Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
			}
		}
		return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
	}

阶段四:应用上下文创建和准备上下文

准备ioc容器
spring上下文前置处理,

	private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
			SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
		context.setEnvironment(environment);
		postProcessApplicationContext(context);
		//应用上下文的初始化器,执行一些初始化操作
		applyInitializers(context);
		listeners.contextPrepared(context);
		//打印启动日志信息
		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)
					.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
		}
		if (this.lazyInitialization) {
			context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
		}
		// Load the sources
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		load(context, sources.toArray(new Object[0]));
		listeners.contextLoaded(context);
	}

总结 一下:

详细流程:


## Spring启动流程分为构造流程和运行流程
构造阶段
 1. 主要是扫描所有spring.factory文件中的key为ApplicationContextInitializerApplicationListener,并进行实例化
运行阶段
 2. 启动计时器
 3. 初始化异常报告的集合
 4. 准备环境
      4.1 获取ConfigurationEnvironment:获取系统属性,javaHome,OS标识,系统的一些属性.....
      4.2 回调在构造阶段中加载的监听器中支持环境准备阶段的监听器
 5,设置忽略的Bean:spring.beaninfo,ignore
 6,打印banner信息
 7,创建容器:根据环境不同,选择不同的容器,并设置一些基本属性,reader,scanner,environment....
 8,准备IOC容器,初始化上下文
 9,刷新容器
 10,刷新容器后的扩展接口

简单流程:

第一步:获取并启动监听器
第二步:构造容器环境
第三步:创建容器
第四步:实例化SpringBootExceptionReporter.class,用来支持报告关于启动的错误
第五步:准备容器
第六步:刷新容器
第七步:刷新容器后的扩展接口
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot启动流程可以分为以下几个步骤: 1. 应用主入口:Spring Boot的应用主入口是一个标有@SpringBootApplication注解的类。在启动过程中,它会被作为启动类加载到内存中。 2. 配置形式:Spring Boot提供了多种配置Bean的形式。首先是通过定义Bean的方式,在应用主入口类中使用@Bean注解来定义Bean。其次是通过@Configuration类配置方式,在应用主入口类外创建一个专门用于配置Bean的类,并在该类中使用@Bean注解来定义Bean。还有一种方式是通过Spring XML配置文件进行配置。最后,还可以通过自动配置类来配置Bean,这些自动配置类是Spring Boot内部提供的,会根据配置文件和依赖自动完成一些配置工作。 3. 启动流程:在启动阶段,Spring Boot会依次执行以下步骤: - 加载Spring Boot的核心配置文件和依赖的配置文件。 - 创建并初始化Spring的ApplicationContext容器。 - 执行各个自动配置类,完成自动配置工作。 - 执行应用主入口类中的初始化方法,并启动Spring Boot应用。 4. Bean定义加载顺序:在Spring Boot启动过程中,Bean的加载顺序非常重要。如果在主线程加载Bean的同时,有异步线程进行Dubbo调用或加载Bean,可能会导致死锁。为了避免这种情况,应该保证只有一个线程在进行Spring加载Bean的操作。可以在Spring启动完成后再进行异步初始化操作,或者使用Spring的事件机制,在订阅ApplicationStartedEvent事件后再执行异步初始化操作。 综上所述,Spring Boot启动流程包括应用主入口、配置形式、启动流程和Bean定义加载顺序。在启动过程中,需要注意Bean的加载顺序,以避免死锁情况的发生。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值