Springboot的启动原理详细解读

 更新时间:2023年11月08日 10:35:24   作者:你就像甜甜的益达  
这篇文章主要介绍了Springboot的启动原理详细解读,springboot项目一般都是打包成jar包直接运行main方法启动,当然也可以跟传统的项目一样打包war包放在tomcat里面启动.那么springboot怎么直接通过main方法启动呢,需要的朋友可以参考下

springboot主函数

springboot项目一般都是打包成jar包直接运行main方法启动,当然也可以跟传统的项目一样打包war包放在tomcat里面启动.那么springboot怎么直接通过main方法启动呢?

举个栗子,这是一个简单的main方法启动类:

@EnableAsync
@EnableScheduling
@EnableTransactionManagement
@EnableConfigurationProperties
@EnableCaching
@MapperScan(value = {"com.study.springbootplus.**.mapper"})
@SpringBootApplication
public class SpringBootPlusApplication {

  public static void main(String[] args) {
    ConfigurableApplicationContext context = SpringApplication.run(SpringBootPlusApplication.class, args);
  }

}

main方法

main方法主要就是看SpringApplication的run方法,这个方法大概就是创建个spring容器,然后创建个web容器(tomcat,jetty等)启动.

run方法点进去,这里一个新建springApplication实例,一个run方法:

	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");
		// main方法中的args参数,可接收命令行启动时添加的参数
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		// 确认spring容器类型
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		//加载ApplicationContextInitializer类,ApplicationContext初始化类
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		//加载ApplicationListener类,监听类
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		//获取main方法所在类
		this.mainApplicationClass = deduceMainApplicationClass();
	}

	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;
	}

SpringApplication.run方法

源码:

public ConfigurableApplicationContext run(String... args) {
//计时
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		//spring容器
		ConfigurableApplicationContext context = null;
		//错误回调
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		//设置一些系统属性
		configureHeadlessProperty();
		//获取启动时监听器
		SpringApplicationRunListeners listeners = getRunListeners(args);
		//启动监听器
		listeners.starting();
		try {
		//获取一些启动参数
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			//创建运行环境environment
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
			//设置一些系统参数
			configureIgnoreBeanInfo(environment);
			//打印banner
			Banner printedBanner = printBanner(environment);
			//创建spring容器
			context = createApplicationContext();
			//获取异常报告,回调
			exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
					//准备容器
			prepareContext(context, environment, listeners, applicationArguments, printedBanner);
			//刷新容器
			refreshContext(context);
			//spring容器后置处理
			afterRefresh(context, applicationArguments);
			//计时终止
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
			//结束通知
			listeners.started(context);
			//执行runner
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {
		//spring容器就绪通知
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		//返回容器
		return context;
	}

getRunListeners方法,starting方法,获取启动监听,和启动

源码:

	private SpringApplicationRunListeners getRunListeners(String[] args) {
		Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
		return new SpringApplicationRunListeners(logger,
				getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
	}

	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;
	}

前面讲过启动的时候会先加载spring会加载所有jar包下的META-INF/spring.factories,然后缓存起来,这里就获取

在这里插入图片描述

返回spirngboot唯一实现SpringApplicationRunListener的接口:EventPublishingRunListener,然后执行starting方法;starting方法就是构建ApplicationStartingEvent,然后获取listener执行,这一块其实还比较复杂.用到了适配器模式:

在这里插入图片描述

prepareEnvironment准备环境

//准备启动参数就不说了
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);


	private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// Create and configure the environment
		//创建一个environment对象
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		//配置环境 PropertySources Profiles
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		//PropertySources
		ConfigurationPropertySources.attach(environment);
		//环境准备
		listeners.environmentPrepared(environment);
		//将环境绑定到oSpringApplication(
		bindToSpringApplication(environment);
		if (!this.isCustomEnvironment) {
			environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
					deduceEnvironmentClass());
		}
		//配置PropertySources-如果有attach到environment
		ConfigurationPropertySources.attach(environment);
		return environment;
	}

createApplicationContext创建容器

	protected ConfigurableApplicationContext createApplicationContext() {
		//获取上下文的类
		Class<?> contextClass = this.applicationContextClass;
		if (contextClass == null) {
			try {
				switch (this.webApplicationType) {
					//判断Web应用类型
				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);
	}

在这里插入图片描述

prepareContext,准备容器

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
			SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
			//设置环境
		context.setEnvironment(environment);
		//处理上下文
		postProcessApplicationContext(context);
		//获取所有ApplicationContextInitializer,执行ApplicationContextInitializer的init方法
		applyInitializers(context);
		//调用SpringApplicationRunListener的contextPrepared,表示容器已经准备
		listeners.contextPrepared(context);
		//日志
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}
		// Add boot specific singleton beans
		//获取beanfactory,注册相关bean
		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]));
		//调用SpringApplicationRunListener的contextPrepared,表示容器已经loaded事件
		listeners.contextLoaded(context);
	}

refreshContext,afterRefresh刷新容器,刷新容器之后执行方法

就是spring容器的刷新方法

到此这篇关于Springboot的启动原理详细解读的文章就介绍到这了,更多相关Springboot启动原理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java如何获得redis所有的key-value

    java如何获得redis所有的key-value

    这篇文章主要介绍了java如何获得redis所有的key-value,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-06-06
  • Java的“Goto”与标签及使用详解

    Java的“Goto”与标签及使用详解

    goto在Java中是一个保留字,但在语言中并没有用到它;Java没有goto。接下来通过本文给大家介绍Java的“Goto”与标签,感兴趣的朋友跟随小编一起看看吧
    2018-10-10
  • JAVA基础面试题整理

    JAVA基础面试题整理

    在本篇文章里小编给大家整理的是关于JAVA基础面试题的整理内容,需要的朋友们可以参考下。
    2019-10-10
  • 使用@PathVariable接收两个参数

    使用@PathVariable接收两个参数

    这篇文章主要介绍了使用@PathVariable接收两个参数的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08
  • 关于IDEA2020.1新建项目maven PKIX 报错问题解决方法

    关于IDEA2020.1新建项目maven PKIX 报错问题解决方法

    这篇文章主要介绍了关于IDEA2020.1新建项目maven PKIX 报错问题解决方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-06-06
  • mybatis返回list<Integer>时resultType写Integer问题

    mybatis返回list<Integer>时resultType写Integer问题

    这篇文章主要介绍了mybatis返回list<Integer>时resultType写Integer问题,具有很好的参考价值,希望对大家有所帮助,
    2023-12-12
  • kettle中使用js调用java类的方法

    kettle中使用js调用java类的方法

    这篇文章主要介绍了kettle中使用js调用java类的方法,本文讲解了注意事项和调用语法,需要的朋友可以参考下
    2015-05-05
  • SpringBoot中自动配置原理解析

    SpringBoot中自动配置原理解析

    SpringBoost是基于Spring框架开发出来的功能更强大的Java程序开发框架,本文将以广角视觉来剖析SpringBoot自动配置的原理,涉及部分Spring、SpringBoot源码,需要的可以参考下
    2023-11-11
  • 分析java并发中的wait notify notifyAll

    分析java并发中的wait notify notifyAll

    一个线程修改一个对象的值,而另一个线程则感知到了变化,然后进行相应的操作,这就是wait()、notify()和notifyAll()方法的本质。本文将详细来介绍它们概念实现以及区别
    2021-06-06
  • Java Volatile 变量详解及使用方法

    Java Volatile 变量详解及使用方法

    这篇文章主要介绍了Java Volatile 变量详解及使用方法的相关资料,需要的朋友可以参考下
    2017-02-02

最新评论