spring boot启动原理

Springboot 使得我们日常开发变得方便,下面一起来看下他的启动原理(往往是跟读取自动配置类联系在一起的)
一、创建SpringApplication
1.调用SpringApplication.run 启动springboot

public static void main(String[] args) {
        SpringApplication.run(DeomoApplication.class, args);
    }

调用springApplication 的run 方法

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
		return run(new Class<?>[] { primarySource }, args);
	}

然后调用 run 实例化了一个自定义 springApplication

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
		return new SpringApplication(primarySources).run(args);
	}

然后调用springApplication 方法

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));//将启动类放到primarySources 里面
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}//推断Main方法所在的类 就是获取Springboot项目的启动类路径

getSpringFactoriesInstances 跟进这个方法

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = getClassLoader();
		// Use names and ensure unique to protect against duplicates
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

这个方法主要就是从类路径jar 包下读取spring.factories 中读取key=ApplicationContextInitializer 的相关类

在这里插入图片描述


	private Class<?> deduceMainApplicationClass() {
		try {
			StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
			for (StackTraceElement stackTraceElement : stackTrace) {
				if ("main".equals(stackTraceElement.getMethodName())) {
					return Class.forName(stackTraceElement.getClassName());
				}
			}
		}
		catch (ClassNotFoundException ex) {
			// Swallow and continue
		}
		return null;
	}

在这里插入图片描述
总结:
1.获取启动类
2.获取web 应用类型(servlet)
3.读取了对外扩展的ApplicationContextInitializer ApplicationListener
4.根据Main 推算出所在的类

二、启动 run


	/**
	 * Run the Spring application, creating and refreshing a new
	 * {@link ApplicationContext}.
	 * @param args the application arguments (usually passed from a Java main method)
	 * @return a running {@link ApplicationContext}
	 */
	public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();//记录开始时间
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();//开启Hander 模式
		SpringApplicationRunListeners listeners = getRunListeners(args);//获取监听器,springboot 在启动会对外扩展很多监听器
		listeners.starting();//发布ApplicationStartingEvent 事件
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);//读取命令行参数,封装成一个ApplicationArguments 对象
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);//预初始化环境
			configureIgnoreBeanInfo(environment);//实现的BeanInfo springboot在扫描容器的时候会忽略
			Banner printedBanner = printBanner(environment);//打印springboot 启动图标
			context = createApplicationContext();//根据webApplication创建上下文
			exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);//从spring.factories 中读取相关类
			prepareContext(context, environment, listeners, applicationArguments, printedBanner);//预初始化容器上下文
			//调用springIoc 加载 该方法为Spring 中非常重要的方法
			//spring boot 是利用ServletWebserverApplication 对spring 做的扩展,所以这里使用AnnotationConfigServletWebserverApplicationContext 启动Spring 容器
			//做了哪些扩展:finishBeanFactoryInitialization加载启动配置类,onRefresh创建servlet 容器 这里不太理解的可以去看下Spring Ioc 加载流程源码,这里不做过多介绍了
			refreshContext(context);
			afterRefresh(context, applicationArguments);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
			listeners.started(context);
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

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

在这里插入图片描述

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// Create and configure the environment
		ConfigurableEnvironment environment = getOrCreateEnvironment();//根据webApplicationType 创建environment 读取java 环境变量和系统环境变量
		configureEnvironment(environment, applicationArguments.getSourceArgs());//将命令行参数读取到环境变量中
		ConfigurationPropertySources.attach(environment);//将@PropertiesSources 的配置信息放到第一行,因为他的优先级是最低的
		listeners.environmentPrepared(environment);//发布监听器,读取springboot 的全局配置文件
		bindToSpringApplication(environment);//将spring.main 开头的配置信息绑定到springApplication
		if (!this.isCustomEnvironment) {
			environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
					deduceEnvironmentClass());
		}
		ConfigurationPropertySources.attach(environment);
		return environment;
	}
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();//获取spring山下问Bean 工厂,负责创建Bean
	beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
	if (printedBanner != null) {
		beanFactory.registerSingleton("springBootBanner", printedBanner);
	}
	//在spring 如果出现了两个相同的Bean ,则后面的会覆盖前面的
	//而在springboot中,设置了不允许覆盖,如果出现了,直接抛异常
	if (beanFactory instanceof DefaultListableBeanFactory) {
		((DefaultListableBeanFactory) beanFactory)
				.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
	}
	//设置当前spring容器是否将所有的Bean 设置为懒加载
	if (this.lazyInitialization) {
		context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
	}
	// Load the sources
	Set<Object> sources = getAllSources();
	Assert.notEmpty(sources, "Sources must not be empty");
	//读取主启动类,后续要根据配置类解析配置的所有Bean
	load(context, sources.toArray(new Object[0]));
	//读取完整配置类后发送SpringApplicationPreparedEvent
	listeners.contextLoaded(context);
}

总结:
1.初始化springApplication 从spring.factories 中读取listener springapplicationContextinitializers
2.运行run
3.读取环境变量,配置信息
4.创建SpringApplication 上下文 :ServletWebServerApplication 内嵌tomcat
5.初始化上下文,读取启动类
6.调用ioc refresh
加载所有的自动配置类,创建servlet容器
7.对外提供监听器提供扩展

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值