springboot源码分析-启动流程分析
2021/10/4 14:11:19
本文主要是介绍springboot源码分析-启动流程分析,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
1.概述
springboot初始化,每次运行springboot项目都会在main方法里调用
SpringApplication.run(SpringApplication.class, args);
初始化也就是new SpringApplication的过程,初始化会设置当前的运行的一些属性
运行时调用run方法的过程,运行基本上就是创建一个上下文环境加载bean
2.源码
https://blog.csdn.net/qq_39482039/article/details/120603123
3.初始化流程
首先在springApplication这个类里有个静态方法给我们调用,其实就是new了一个SpringApplication并调用它的run方法
/** * 可用于运行 {@link SpringApplication} 的静态助手 * 使用默认设置指定源。 * * @param primarySource the primary source to load * @param args the application arguments (usually passed from a Java main method) * @return the running {@link ApplicationContext} */ public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) { return run(new Class<?>[]{primarySource}, args); } /** * 可用于运行 {@link SpringApplication} 的静态助手 * 使用默认设置和用户提供的参数指定源。 * * @param primarySources the primary sources to load * @param args the application arguments (usually passed from a Java main method) * @return the running {@link ApplicationContext} */ public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); }
初始化看new SpringApplication方法
/** * 创建一个新的 {@link SpringApplication} 实例。应用程序上下文将加载 * 来自指定主要来源的 bean(参见 {@link SpringApplication class-level} * 有关详细信息的文档。调用前可以自定义实例 * {@link #run(String...)}。 * * @param resourceLoader the resource loader to use * @param primarySources the primary bean sources * @see #run(Class, String[]) * @see #setSources(Set) */ @SuppressWarnings({"unchecked", "rawtypes"}) public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { //设置当前的资源加载器 this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); //设置当前的启动入口 this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); //设置web环境类型, this.webApplicationType = WebApplicationType.deduceFromClasspath(); //从spring.factories文件中加载ApplicationContextInitializer初始化器 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); //从spring.factories文件中加载ApplicationListener初始化器 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); //根据堆栈跟踪信息获取执行的main方法的class this.mainApplicationClass = deduceMainApplicationClass(); }
确定当前应用程序的环境
static WebApplicationType deduceFromClasspath() { //如果有DispatcherHandler这个类能被加载并且DispatcherServlet和ServletContainer不能加载,当前就是反应式应用程序 if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null) && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) { return WebApplicationType.REACTIVE; } //如果没有ConfigurableWebApplicationContext和Servlet类不是web应用和反应式应用程序 for (String className : SERVLET_INDICATOR_CLASSES) { if (!ClassUtils.isPresent(className, null)) { return WebApplicationType.NONE; } } //否则就是基于servlet的Web应用程序 return WebApplicationType.SERVLET; }
根据堆栈跟踪信息获取执行的main方法的class
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; }
4.运行流程
根据当前环境创建不同的上下文,
如果是web环境创建AnnotationConfigServletWebServerApplicationContext
如果是反应式环境创建AnnotationConfigReactiveWebServerApplicationContext
如果是默认创建AnnotationConfigApplicationContext
/** * 运行 Spring 应用程序,创建并刷新一个新的 * {@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(); //在spring.factories文件获取配置的监听器 SpringApplicationRunListeners listeners = getRunListeners(args); //调用所有监听器的starting方法 listeners.starting(); try { //将参数封装为applicationArguments对象 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); //创建配置环境 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); //环境中设置忽略的属性 configureIgnoreBeanInfo(environment); //打印Banner Banner printedBanner = printBanner(environment); //创建webServlet上下文环境 context = createApplicationContext(); //在spring.factories文件获取SpringBootExceptionReporter的配置 exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context); //准备上下文环境 prepareContext(context, environment, listeners, applicationArguments, printedBanner); //刷新上下文环境 refreshContext(context); //刷新后处理 afterRefresh(context, applicationArguments); //计时器结束 stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } //执行监听器的started运行方法 listeners.started(context); //通知runner,启动后执行 callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { //执行监听器的running方法 listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } return context; }
创建上下文环境
/** * 用于创建 {@link ApplicationContext} 的策略方法。默认这个 * 方法将尊重任何明确设置的应用程序上下文或应用程序上下文 * 回退到合适的默认值之前的类。 * * @return the application context (not yet refreshed) * @see #setApplicationContextClass(Class) */ 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); }
准备上下文环境
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { //设置上下文环境 context.setEnvironment(environment); //上下文后处理,子类可对其进行拓展 postProcessApplicationContext(context); //调用容器初始化监听器接口,ApplicationContextInitializer applyInitializers(context); //容器上下文准备时调用 listeners.contextPrepared(context); if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // 添加启动特定的单例 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); } //如果延迟加载设置添加beanFactory后置处理器LazyInitializationBeanFactoryPostProcessor if (this.lazyInitialization) { context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()); } //获取所有的源,源是在初始化的时候源就是传入的实现了main方法启动的class //这里也可以传入其他的类型, //一般用的是javaConfig //可以是处理Package,SpringApplication.run(Package.getPackage("com.leone.chapter.profiles"), args); //可以是处理Resource,new ClassPathResource("applicationContext.xml") //可以是处理String类型,SpringApplication.run("classpath:/applicationContext.xml", args); Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); load(context, sources.toArray(new Object[0])); //调用spring运行监听器的contextLoaded方法 listeners.contextLoaded(context); }
刷新上下文,调用上下文的refresh方法
private void refreshContext(ConfigurableApplicationContext context) { refresh(context); if (this.registerShutdownHook) { try { context.registerShutdownHook(); } catch (AccessControlException ex) { // Not allowed in some environments. } } } /** * 刷新底层 {@link ApplicationContext}。 * * @param applicationContext the application context to refresh */ protected void refresh(ApplicationContext applicationContext) { Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext); ((AbstractApplicationContext) applicationContext).refresh(); }
这篇关于springboot源码分析-启动流程分析的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23Springboot应用的多环境打包入门
- 2024-11-23Springboot应用的生产发布入门教程
- 2024-11-23Python编程入门指南
- 2024-11-23Java创业入门:从零开始的编程之旅
- 2024-11-23Java创业入门:新手必读的Java编程与创业指南
- 2024-11-23Java对接阿里云智能语音服务入门详解
- 2024-11-23Java对接阿里云智能语音服务入门教程
- 2024-11-23JAVA对接阿里云智能语音服务入门教程
- 2024-11-23Java副业入门:初学者的简单教程
- 2024-11-23JAVA副业入门:初学者的实战指南