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源码分析-启动流程分析的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程