springboot学习笔记8-1启动原理(springboot2.3.1版本)

通过查看springboot主配置类的main方法中,调用的是springApplication中的静态的run方法

@SpringBootApplication
public class SpringbootWebRestfulcrudApplication {

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

进入到run方法中发现,springboot启动分为俩步,一个是实例化SpringApplication,将主配置类存到class数组中作为构造方法参数传进去

	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");
		//将主配置类存到一个HashSet集合中,赋值
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		//同样也是给属性赋值,这个是web程序的属性,在WebApplicationType中会有三个属性NONE,SERVLET,REACTIVE
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		//给initializers属性赋值,在getSpringFactoriesInstances里面我们可以找到调用了loadSpringFactories方法,最后获取的jar包中META-INF包下的Apring.factories这个配置文件中的ApplicationContextInitializer对应的属性
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		//给listeners赋值,一样的道理,获取的是ApplicationListener对应的属性
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}

这个deduceFromClasspath()方法中最重要的功能 是加载class功能,而class加载是依赖jar包是否引起而判断的,所以如果引入了javax.servlet.Servlet的jar,则会启动Servlet模式,如果引入的jar是spring-boot-starter-webflux,而且没引入servlet相关的jar,则会启动Reactive模式。

//判断当前的应用服务属于什么类型
static WebApplicationType deduceFromClasspath() {
		if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
				&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
			return WebApplicationType.REACTIVE;
		}
		for (String className : SERVLET_INDICATOR_CLASSES) {
			if (!ClassUtils.isPresent(className, null)) {
				return WebApplicationType.NONE;
			}
		}
		return WebApplicationType.SERVLET;
	}
private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
			"org.springframework.web.context.ConfigurableWebApplicationContext" };
private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";

	private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";

loadSpringFactories里面可以看到获取了FACTORIES_RESOURCE_LOCATION属性,这个FACTORIES_RESOURCE_LOCATION属性就是"META-INF/spring.factories"也就是说获取META-INF包下的spring.factories文件里面ApplicationContextInitializer属性对应的值以集合的形式返回赋值给initializers属性

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
		MultiValueMap<String, String> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		try {
			Enumeration<URL> urls = (classLoader != null ?
					classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
			result = new LinkedMultiValueMap<>();
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryTypeName = ((String) entry.getKey()).trim();
					for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
						result.add(factoryTypeName, factoryImplementationName.trim());
					}
				}
			}
			cache.put(classLoader, result);
			return result;
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	}

最后调用的deduceMainApplicationClass方法,作用是获取主配置类的main方法,将这个类信息返回

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

第二步:运行程序

	public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();
		//这里是从META-INF下spring.factories里面获取SpringApplicationRunListener对应的值
		SpringApplicationRunListeners listeners = getRunListeners(args);
		//这里遍历所有的SpringApplicationRunListeners,并且执行里面的starting方法
		listeners.starting();
		try {
			//封装命令行参数
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			//准备环境(先创建环境,创建完成后,回调SpringApplicationRunListeners里面的environmentPrepared方法,表示环境准备完成)
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
			configureIgnoreBeanInfo(environment);
			Banner printedBanner = printBanner(environment);
			// 创建ioc容器,根据webApplicationType里面的属性来判断是创建web环境还是其他的
			context = createApplicationContext();
			//做异常分析
			exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
			//准备上下文环境,给ioc容器中的属性赋值
			prepareContext(context, environment, listeners, applicationArguments, printedBanner);
			//刷新容器,ioc容器初始化(如果是web容器还会创建嵌入式的tomcat)
			//扫描,创建,加载,所有组件的地方(配置类,组件,自动配置)
			refreshContext(context);
			//这个在牢版本里面调用了callRunners方法,新版本里面为空方法,callRunners在下面有调用
			afterRefresh(context, applicationArguments);
			//在这里,1.0版本有调用SpringApplicationRunListeners里面的finish方法,新版本的SpringApplicationRunListeners里面已经没有了finish方法
			//保存当前应用状态
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
			//在ioc容器创建完成,保存状态之后执行该方法
			//执行SpringApplicationRunListeners里面的started方法(在1.0版本没有这个步骤)
			listeners.started(context);
			//从ioc里面获取所有的ApplicationRunner和CommandLineRunner,遍历回调,先将所有的ApplicationRunner回调完,再回调CommandLineRunner
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			//在这里面会执行SpringApplicationRunListeners的failed方法(1.0版本没有该方法)
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {
			//在callRunners方法执行完,ApplicationRunner,CommandLineRunner加载完之后执行,类似于老版本的finish方法
			// 执行SpringApplicationRunListeners里面的running方法(在1.0版本没有这个步骤)
			listeners.running(context);
		}
		catch (Throwable ex) {
			//在这里面会执行SpringApplicationRunListeners的failed方法(1.0版本没有该方法)
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		//整个spring应用完成后,返回spring的ioc容器
		return context;
	}

准备环境

	private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// Create and configure the environment
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		ConfigurationPropertySources.attach(environment);
		listeners.environmentPrepared(environment);
		bindToSpringApplication(environment);
		if (!this.isCustomEnvironment) {
			environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
					deduceEnvironmentClass());
		}
		ConfigurationPropertySources.attach(environment);
		return environment;
	}

创建ioc容器,根据webApplicationType里面的属性来判断(SERVLET,REACTIVE),最后使用BeanUtils反射创建ioc容器

	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) {
		//给ioc容器中的environment赋值(配置环境)
		context.setEnvironment(environment);
		//ioc的后置组件
		postProcessApplicationContext(context);
		//之前new SpringApplication的时候,已经从各个jar包的META-INF下的spring.favtories里面获取到的ApplicationContextInitializer,调用里面的initialize方法
		applyInitializers(context);
		//listeners还要回调contextPrepared这个方法
		listeners.contextPrepared(context);
		//在listeners准备执行完成之后,还要调用ApplicationContextInitializer里面的contextLoaded方法
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}
		// Add boot specific singleton beans
		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]));
		listeners.contextLoaded(context);
	}
protected void applyInitializers(ConfigurableApplicationContext context) {
		for (ApplicationContextInitializer initializer : getInitializers()) {
			Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
					ApplicationContextInitializer.class);
			Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
			initializer.initialize(context);
		}
	}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值