springboot学习记录(一)--探究springboot的启动流程

探究springboot的启动流程

使用springboot搭建一个web框架非常方便,只需要简单的几步即可:

  • 一、在maven依赖中添加相应的parent依赖和web的starter依赖
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.4.1</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
  • 二、建立主启动类,并在Class上标注@SpringBootApplication注解
@SpringBootApplication
public class MainApplication {
}
  • 三、在启动类的main入口方法中直接调用SpringApplication的run方法进行启动
public static void main(String[] args) {
    SpringApplication application = new SpringApplication(MainApplication.class) ;

    ConfigurableApplicationContext run = application.run(args);

}

在main方法中的启动过程中,我们可以看到程序做了如下操作:

  • 一、实例化一个SprnigApplication对象,调用带有一个参数的构造器进行实例化
  • 二、调用实例化出的SpringApplication对象的run方法,传入的参数为主入口方法的参数,返回的值为一个ConfigurableApplicatonContext对象,也就是我们的IOC容器

首先,我们来看看SpringApplication对象在通过构造器进行实例化的过程中做了什么:

@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));
	this.webApplicationType = WebApplicationType.deduceFromClasspath();
	this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class));
	setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
	this.mainApplicationClass = deduceMainApplicationClass();
}

在源码中可以看出,在实例化SpringApplicaton的过程中做了如下几件事情:

  • 1、加载resourceLoader资源加载器,目前为null,经过查看ResourceLoader接口的实现类,实现此接口的类基本都为各种的ApplicationContext

  • 2、通过Assert断言来检查传入的primarySources不为null

  • 3、将primarySources封装为一个LinkedHashSet,并赋给primarySources属性

  • 4、给对象中的webApplicationType属性进行赋值,此属性表示web应用的类型,跟踪代码,webApplicationType有3种类型,分别为:(本例中,应为我们引入的是starter-web,因此是传统的SERVLET应用)

    • NONE 非web应用
    • SERVLET 传统servlet的web应用
    • REACTIVE webflux 响应式web应用
  • 5、加载bootstrappers启动引导

  • 6、加载初始化器

  • 7、加载监听器

  • 8、推断主应用类,并赋值给mainApplicationClass元素,本例中,依然为主启动类

以上bootstrappers、初始化器和监听器的加载方式,都为调用getSpringFactoriesInstances()方法来,此方法具体做的处理为:读取META-INF/spring.factories文件内容,文件内容中定义了相应的启动引导、初始化器和监听器对应的对象的全类名,springboot读取到这些全类名后,再调用createSpringFactoriesInstances()方法 ,根据全类名,通过反射的方式进行对象的实例化。

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

private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
			ClassLoader classLoader, Object[] args, Set<String> names) {
	List<T> instances = new ArrayList<>(names.size());
	for (String name : names) {
		try {
			Class<?> instanceClass = ClassUtils.forName(name, classLoader);
			Assert.isAssignable(type, instanceClass);
			Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
			T instance = (T) BeanUtils.instantiateClass(constructor, args);
			instances.add(instance);
		}
		catch (Throwable ex) {
			throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
		}
	}
	return instances;
}


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

	result = new HashMap<>();
	try {
		Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
		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();
				String[] factoryImplementationNames =
						StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
				for (String factoryImplementationName : factoryImplementationNames) {
					result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
							.add(factoryImplementationName.trim());
				}
			}
		}

		// Replace all lists with unmodifiable lists containing unique elements
		result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
				.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
		cache.put(classLoader, result);
	}
	catch (IOException ex) {
		throw new IllegalArgumentException("Unable to load factories from location [" +
				FACTORIES_RESOURCE_LOCATION + "]", ex);
	}
	return result;
}

SpringApplication对象初始化后,进行run方法的调用来启动应用

public ConfigurableApplicationContext run(String... args) {
	StopWatch stopWatch = new StopWatch();
	stopWatch.start();
	DefaultBootstrapContext bootstrapContext = createBootstrapContext();
	ConfigurableApplicationContext context = null;
	configureHeadlessProperty();
	SpringApplicationRunListeners listeners = getRunListeners(args);
	listeners.starting(bootstrapContext, this.mainApplicationClass);
	try {
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
		ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
		configureIgnoreBeanInfo(environment);
		Banner printedBanner = printBanner(environment);
		context = createApplicationContext();
		context.setApplicationStartup(this.applicationStartup);
		prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
		refreshContext(context);
		afterRefresh(context, applicationArguments);
		stopWatch.stop();
		if (this.logStartupInfo) {
			new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
		}
		listeners.started(context);
		callRunners(context, applicationArguments);
	}
	catch (Throwable ex) {
		handleRunFailure(context, ex, listeners);
		throw new IllegalStateException(ex);
	}

	try {
		listeners.running(context);
	}
	catch (Throwable ex) {
		handleRunFailure(context, ex, null);
		throw new IllegalStateException(ex);
	}
	return context;
}

从以上的代码中,我们看到springboot主要做了如下几件事情:

  • 1、加载环境Environment
  • 2、打印banner
  • 3、创建上下文context,本例中创建的是AnnotationConfigServletWebServerApplicationContext
  • 4、准备context,为context中做相应的初始化操作
  • 5、refreshContext,此处极为springIOC容器的初始化过程,基本就是调用applicationContext的refresh()方法
  • 6、发布启动事件,相应listener的处理
  • 7、callRunners,调用IOC容器中所有实现了ApplicationRunner接口或者CommandLineRunner接口的bean,可以扩展应用启动之后的一些自定义处理。
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值