springboot源码解析(1)一个springboot程序的启动及简单分析

1. 一个springboot程序的简单启动

如果我们想要启动一个springboot程序,只需要以下两步。

  1. pom文件引入相关依赖
       <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <version>2.3.4.RELEASE</version>
        </dependency>
  1. 编写启动类
@SpringBootApplication
public class App {
    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(App.class);
        application.run(args);
        //或者用下面这一行代码,跟上面两行代码的作用是一样的。
        //SpringApplication.run(App.class, args);
    }
}

2. new SpringApplication(App.class)中发生了什么?

  1. resourceLoader ,这是spring加载资源的统一入口。后续专题讲解。
  2. primarySources,此处会对主配置源参数做初始化,后续注册到容器时会用到。后续专题讲解。
  3. 判断并设置启动类型:现有NONE、SERVLET、REACTIVE三种,我们这里是SERVLET
  4. 寻找ApplicationContextInitializer的配置并实例化。
  5. 寻找ApplicationListener的配置并实例化。
  6. 获取启动类。也就是启动main方法的那个类。
    ......
    //构造方法会初始化一下5个属性的值。
    private ResourceLoader resourceLoader;
    private Set<Class<?>> primarySources;
    private List<ApplicationContextInitializer<?>> initializers;
    private Class<?> mainApplicationClass;
	private List<ApplicationListener<?>> listeners;
	......
    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        //resourceLoader 是做什么的,后续专题分析。总的来说就是spring框架用来加载资源的一套接口。
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources `= new LinkedHashSet<>(Arrays.asList(primarySources));
		//判断启动类型(NONE、SERVLET、REACTIVE三种)
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		//从META-INF/spring.factories文件获取对应ApplicationContextInitializer的配置,并实例化。
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		//从META-INF/spring.factories文件获取对应ApplicationListener的配置,并实例化。
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		//获取启动类。也就是启动main方法的那个类。
		this.mainApplicationClass = deduceMainApplicationClass();
	}
2.1 getSpringFactoriesInstances()方法解析

这个方法或根据入参,在META-INF/spring.factories文件中找到相应类的配置,实例化相应对象并返回。具体可以自己去看源码。

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;
	}
2.2 ApplicationContextInitializerApplicationListener是什么?

从上文,我们知道启动是以上相应的实例到SpringApplication对象中,那么这里到底实例化了哪些对象,又各有什么作用。默认实例化了一下7个ApplicationContextInitializer和11个ApplicationListener对象,具体作用,后续用到在进行详解。下面我们开始分析 ApplicationContextInitializerApplicationListener两个接口。
默认加载的容器初始化对象和监听器

2.2.1 ApplicationContextInitializer接口详解

这个接口的官方解释大致如下:

  • 用于在spring容器刷新之前初始化Spring ConfigurableApplicationContext的回调接口。(剪短说就是在容器刷新之前调用该类的 initialize 方法。并将 ConfigurableApplicationContext 类的实例传递给该方法)
  • 通常用于需要对应用程序上下文进行编程初始化的web应用程序中。例如,根据上下文环境注册属性源或激活配置文件等。
  • 可排序的(实现Ordered接口,或者添加@Order注解)

总的来说,就是在spring容器刷新前,对传入的容器applicationContext进行一些初始化的操作,具体的调用位置后面再说。

public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {

	/**
	 * Initialize the given application context.
	 * @param applicationContext the application to configure
	 */
	void initialize(C applicationContext);

}
2.2.2 ApplicationListener接口详解

一看就是spring的观察者(监听器)模式。上面注册的11个监听器会在对应事件发出时回调 onApplicationEvent(E event)方法。

public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

	/**
	 * Handle an application event.
	 * @param event the event to respond to
	 */
	void onApplicationEvent(E event);

}

3.application.run(args)-springboot启动最重要的方法

application.run(args)方法是springboot启动过程中最重要的方法,在下一篇中进行详细介绍。

	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 的List<SpringApplicationRunListener> listeners属性中。
		SpringApplicationRunListeners listeners = getRunListeners(args);
		//里面会调用每个SpringApplicationRunListener的starting()方法,SpringApplicationRunListener类的具体作用,下文再进行介绍。
		listeners.starting();
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			//环境准备
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
			configureIgnoreBeanInfo(environment);
			Banner printedBanner = printBanner(environment);
			context = createApplicationContext();
			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);
			}
			listeners.started(context);
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

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

4. 疑问点

4.1 @SpringBootApplication注解到底发挥了什么作用?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值