玩转springboot之springboot启动原理
创始人
2025-01-07 16:33:03
0

启动原理

注意:使用版本为spring-boot-2.2.2.RELEASE

springboot启动的入口肯定是main方法啦,那就从main方法入口走起来看看是如何进行启动的

@SpringBootApplication public class ConsulApp {     public static void main(String[] args) {       	// 调用SpringApplication的静态run方法         SpringApplication.run(ConsulApp.class,args);     } } 

进入main方法

// 这个primarySources是传入进来的启动类 public static ConfigurableApplicationContext run(Class[] primarySources, String[] args) {   // 先实例化SpringApplication    return new SpringApplication(primarySources).run(args); } 

实例化SpringApplication

// this(null, primarySources) // resourceLoader是null,primarySources是传入进来的启动类 public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {    this.resourceLoader = resourceLoader;    Assert.notNull(primarySources, "PrimarySources must not be null");   // 使用set进行去重    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));   // 根据classpath中是否存在org.springframework.web.reactive.DispatcherHandler来判断是否为REACTIVE   // 根据classpath中是否存在"javax.servlet.Servlet"和"org.springframework.web.context.ConfigurableWebApplicationContext"来判断是否为SERVLET   // web应用的类型,是None表示非web项目  SERVLET表示普通的servlet web项目  REACTIVE表示响应式的web项目    this.webApplicationType = WebApplicationType.deduceFromClasspath();   // 设置应用上下文初始化器  SpringFactoriesLoader从META-INF/spring.factories加载的,获取 ApplicationContextInitializer 接口的所有配置的类路径名称,并进行实例化  setInitializers((Collection)getSpringFactoriesInstances(ApplicationContextInitializer.class));   // 设置监听器 SpringFactoriesLoader从META-INF/spring.factories加载的,获取ApplicationListener接口的所有配置的类路径名称,并进行实例化    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));   // 推断主启动类,通过构造一个运行时异常,再遍历异常栈中的方法名,获取方法名为 main 的栈帧,从来得到入口类的名字再返回该类    this.mainApplicationClass = deduceMainApplicationClass(); } 

执行SpringApplication实例的run方法

实例化SpringApplication之后,调用该对象的run方法

  • NO1 进行计时,记录整个过程的加载事件
  • NO2 初始化应用上下文和异常报告集合,设置headless变量
  • NO3 通过SpringFactoriesLoader加载SpringApplicationRunListener监听器,调用starting方法,表示springboot要启动了
  • NO4 创建ConfigurableEnvironment,将配置的环境绑定到spring应用中(包括PropertySource和Profile),并调用SpringApplicationRunListener监听器的environmentPrepared方法,应用的environment已经准备完毕
  • NO5 Banner打印并创建应用上下文
  • NO6 创建应用上下文,根据webApplicationType决定创建不同的上下文
  • NO7 准备应用上下文,执行初始化器ApplicationContextInitializer的initialize方法
  • NO8 刷新应用上下文
  • NO9 计时停止,调用SpringApplicationRunListener监听器的started方法,表示应用上下文已完成
  • NO10 执行所有的Runner运行器(ApplicationRunner和CommandLineRunner)
  • NO11 调用SpringApplicationRunListener监听器的running方法,表示已经开始运行了
public ConfigurableApplicationContext run(String... args) {   // NO1    // 创建计时监控对象,记录整个过程的加载事件    StopWatch stopWatch = new StopWatch();   // 启动计时监控,记录开始时间    stopWatch.start();   // NO2   // 初始化应用上下文和异常报告集合    ConfigurableApplicationContext context = null;    Collection exceptionReporters = new ArrayList<>();      // 设置系统属性 java.awt.headless,默认true    configureHeadlessProperty();   // NO3   // 创建SpringApplicationRunListeners监听器,通过SpringFactoriesLoader加载,监听器在spring.factories中SpringApplicationRunListener接口,默认是只有org.springframework.boot.context.event.EventPublishingRunListener   // 本质是一个事件发布者    SpringApplicationRunListeners listeners = getRunListeners(args);   // 开始监听,表示springboot要开始启动了   // 广播ApplicationStartingEvent事件    listeners.starting();    try {      // NO4      // 初始化默认应用参数类       ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);      // 加载springboot配置环境      // configurePropertySources(environment, args);  配置PropertySource 		// configureProfiles(environment, args);  配置profiles      // 此时广播了一个ApplicationEnvironmentPreparedEvent事件,通知事件监听者,应用的environment已经准备完毕       ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);       configureIgnoreBeanInfo(environment);      // NO5      // Banner打印       Banner printedBanner = printBanner(environment);      // NO6 创建应用上下文,根据webApplicationType应用类型的不同,创建不同的上下文,通过Class.forName的方式      // DEFAULT_CONTEXT_CLASS = "org.springframework.context.annotation.AnnotationConfigApplicationContext"      // DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext"      // DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext"       context = createApplicationContext();            // 异常报告器,在spring.factories中SpringBootExceptionReporter接口       exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,             new Class[] { ConfigurableApplicationContext.class }, context);      // NO7      // 准备应用上下文      // 给ApplicationContext设置environment 		// 遍历调用所有的ApplicationContextInitializer的 initialize()方法     // 广播ApplicationContextInitializedEvent事件,ApplicationContext初始化事件     // 将所有的bean加载到容器中     // 广播ApplicationPreparedEvent事件,ApplicationContext准备事件       prepareContext(context, environment, listeners, applicationArguments, printedBanner);            // NO8      // 刷新应用上下文,获取所有的BeanFactoryPostProcessor对容器进行一些额外操作      // 其中对于@Configuration、@ComponentScan、@Import、@PropertySource、@ImportResource、@Bean注解都是在这里处理的      // 这里的操作就是spring中的refresh方法那一套东西       refreshContext(context);      // 应用上下文刷新后置处理(该方法为空方法)       afterRefresh(context, applicationArguments);      // 停止计时监控       stopWatch.stop();       if (this.logStartupInfo) {         // 输出主类名以及时间信息          new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);       }      // NO9      // 广播ApplicationStartedEvent事件,表示应用上下文已完成       listeners.started(context);      // NO10      // 执行Runner运行器  ApplicationRunner和CommandLineRunner实现类       callRunners(context, applicationArguments);    }    catch (Throwable ex) {       handleRunFailure(context, ex, exceptionReporters, listeners);       throw new IllegalStateException(ex);    }     try {      // NO11      // 发布应用上下文就绪事件       listeners.running(context);    }    catch (Throwable ex) {       handleRunFailure(context, ex, exceptionReporters, null);       throw new IllegalStateException(ex);    }    return context; } 
NO7 准备应用上下文
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,       SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {   // 设置环境    context.setEnvironment(environment);   // 配置上下文的bean生成器以及资源加载器    postProcessApplicationContext(context);   // 上下文初始化器执行initialize方法    applyInitializers(context);   // 触发监听器的contextPrepared事件    listeners.contextPrepared(context);    if (this.logStartupInfo) {       logStartupInfo(context.getParent() == null);       logStartupProfileInfo(context);    }    // Add boot specific singleton beans   // 注册两个特殊的单例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 sources = getAllSources();    Assert.notEmpty(sources, "Sources must not be empty");   // 加载bean    load(context, sources.toArray(new Object[0]));   // 触发监听器的contextLoaded事件    listeners.contextLoaded(context); }  
NO8 刷新应用上下文

这里实际调用的就是spring中的refresh方法 可参考 源码分析之上下文构建

public void refresh() throws BeansException, IllegalStateException {    synchronized (this.startupShutdownMonitor) {       // Prepare this context for refreshing.      // 设置beanFactory的一些属性      // 添加后置处理器      // 设置忽略的自动装配接口      // 注册一些组件       prepareRefresh();        // Tell the subclass to refresh the internal bean factory.       ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();        // Prepare the bean factory for use in this context.       prepareBeanFactory(beanFactory);        try {          // Allows post-processing of the bean factory in context subclasses.          postProcessBeanFactory(beanFactory);           // Invoke factory processors registered as beans in the context.          invokeBeanFactoryPostProcessors(beanFactory);           // Register bean processors that intercept bean creation.          registerBeanPostProcessors(beanFactory);           // Initialize message source for this context.          initMessageSource();           // Initialize event multicaster for this context.          initApplicationEventMulticaster();           // Initialize other special beans in specific context subclasses.          onRefresh();           // Check for listener beans and register them.          registerListeners();           // Instantiate all remaining (non-lazy-init) singletons.          finishBeanFactoryInitialization(beanFactory);           // Last step: publish corresponding event.          finishRefresh();       }        catch (BeansException ex) {                     // Destroy already created singletons to avoid dangling resources.          destroyBeans();           // Reset 'active' flag.          cancelRefresh(ex);           // Propagate exception to caller.          throw ex;       }        finally {          // Reset common introspection caches in Spring's core, since we          // might not ever need metadata for singleton beans anymore...          resetCommonCaches();       }    } } 

https://zhhll.icu/2021/框架/springboot/源码/2.启动原理/

相关内容

热门资讯

透视私人局!wepoker辅助... 透视私人局!wepoker辅助器怎么用,aapoker可以开挂吗,透视教程(有挂细节)1、wepok...
透视科技!大菠萝游戏有挂吗,H... 《HHpoker其实是有挂确实有挂软件透明挂》是一款多人竞技的HHpoker其实是有挂确实有挂辅助透...
wepoker透视!约局吧德州... wepoker透视!约局吧德州有挂吗(透视)其实是有挂(存在挂教程)1、下载好约局吧德州有挂吗辅助软...
透视好友!wepoker透视脚... 透视好友!wepoker透视脚本苹果版,aapoker透视插件下载,透视教程(有挂细节);1、很好的...
透视了解!we poker透视... 透视了解!we poker透视,wepoker发牌规律总结(详细辅助揭秘教程);玩家在wepoker...
wpk辅助购买!wpk俱乐部有... wpk辅助购买!wpk俱乐部有没有辅助(透视)真是真的是有挂(教你攻略);1、wpk俱乐部有没有辅助...
透视app!wpk脚本下载,w... 透视app!wpk脚本下载,wepoker底牌透视脚本http,2025新版教程(有挂教程)1)we...
透视好友房!wepoker究竟... 透视好友房!wepoker究竟有没有挂,HHpoker的辅助是真的吗(详细辅助黑科技教程);原来确实...
透视玄学!德扑辅助工具,wep... 透视玄学!德扑辅助工具,wepoker透视挂安装(详细辅助透牌教程);支持多人共享记分板与复盘,通过...
透视苹果版!wepoker到底... 1、透视苹果版!wepoker到底有没有挂透视,wepokerplus透视挂真的假的(详细辅助202...