The core principle of Spring Boot "1" Spring Boot startup process

This article takes the Spring Boot version 2.0.2.RELEASEas an example to introduce

1. Container startup entry

First enter from SpringBootthe startup process

public class MyApplicationContext {
    
    
	public static void main(String[] args) {
    
    
		// 启动方法入口
		SpringApplication.run(MyApplicationContext.class, args);
	}
}

2. Initialize SpringApplication

public class SpringApplication {
    
    
	public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
    
    
		// <1> 把传入的类作为“primarySource”,即“主要源”
		return run(new Class<?>[]{
    
    primarySource}, args);
	}

	public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    
    
		// <2> 创建 SpringApplication
		return new SpringApplication(primarySources).run(args);
	}
}
  • <1>, take the passed MyApplicationContext parameter as the "main source"
  • <2>At, create SpringApplication

2.1 Constructor

The creation of the SpringApplication object is completed through the constructor, the code is as follows:

public class SpringApplication {
    
    

    public SpringApplication(Class<?>... primarySources) {
    
    
        this(null, primarySources);
    }

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    
    
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        // <1> 决定 webApplicationType 类型
        this.webApplicationType = deduceWebApplicationType();
        // <2> 实例化 ApplicationContextInitializer 类型并设置
        setInitializers((Collection) getSpringFactoriesInstances(
            ApplicationContextInitializer.class));
        // <3> 实例化 ApplicationListener 类型并设置
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        // <4> 决定 main 方法的类
        this.mainApplicationClass = deduceMainApplicationClass();
    }
}
  • <1>, decide webApplicationTypethe type , which type of context will be instantiated later

  • <2>, instantiate ApplicationContextInitializerthe type and set it SpringApplicationinto . This interface is one of the important extension interfaces of Boot, which will be used later

    The ApplicationContextInitializer interface requires a parameter list that is[SpringApplication application, String[] args]constructor, why? look down

    private SpringApplicationRunListeners getRunListeners(String[] args) {
           
           
       // 参数列表。后面会用这个参数列表来查找构造器
       Class<?>[] types = new Class<?>[] {
           
            SpringApplication.class, String[].class };
       return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
           SpringApplicationRunListener.class, types, this, args));
    }
    
    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) {
           
           
           Class<?> instanceClass = ClassUtils.forName(name, classLoader);
           Assert.isAssignable(type, instanceClass);
           Constructor<?> constructor = instanceClass
               .getDeclaredConstructor(parameterTypes);	// <1>
           T instance = (T) BeanUtils.instantiateClass(constructor, args);	// <2>
           instances.add(instance);
       }
       return instances;
    }
    
    • <1>, use the parameter list parameterTypes to find the constructor in type
    • <2>At the place, use the constructor to instantiate. The first parameter of the constructor parameter args is springApplication, and the subsequent parameters are the command line parameter args
  • <3>, instantiate ApplicationListenerthe type and set it SpringApplicationinto . This interface is one of the important extension interfaces of Boot, which will be used later

  • <4>, decide which class to use for the main method

2.1.1 deduceWebApplicationType

The application determines the context type through deduceWebApplicationTypethe method , the code is as follows:

public class SpringApplication {
    
    

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

	private static final String MVC_WEB_ENVIRONMENT_CLASS = "org.springframework."
			+ "web.servlet.DispatcherServlet";

	private static final String[] WEB_ENVIRONMENT_CLASSES = {
    
    "javax.servlet.Servlet",
			"org.springframework.web.context.ConfigurableWebApplicationContext"};

	// 根据类是否存在来决定上下文类型
	private WebApplicationType deduceWebApplicationType() {
    
    
		// <1> 使用reactive类型(如果reactive存在且web不存在)
		if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
				&& !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) {
    
    
			return WebApplicationType.REACTIVE;
		}
		// <2> 使用none类型,也就是普通的应用上下文(如果WEB_ENVIRONMENT_CLASSES都不存在)
		for (String className : WEB_ENVIRONMENT_CLASSES) {
    
    
			if (!ClassUtils.isPresent(className, null)) {
    
    
				return WebApplicationType.NONE;
			}
		}
		// <3> 其他情况使用servet类型
		return WebApplicationType.SERVLET;
	}
}
  • <1>, if REACTIVE_WEB_ENVIRONMENT_CLASS exists and MVC_WEB_ENVIRONMENT_CLASS does not exist, useREACTIVE

  • <2>, if WEB_ENVIRONMENT_CLASSES does not exist, the context type isNONE

  • <3>, other context types use:SERVLET

2.2.2 createApplicationContext

this.webApplicationTypeCreate the application context object of the Spring Boot container according to the context type , the code is as follows:

public class SpringApplication {
    
    
	// web 环境的上下文对象
	public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework.boot."
			+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";

	// reactive 环境的上下文对象
	public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
			+ "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";

	// 非 web 环境的上下文对象
	public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
			+ "annotation.AnnotationConfigApplicationContext";

	// 具体上下文应用哪个类
	protected ConfigurableApplicationContext createApplicationContext() {
    
    
		Class<?> contextClass = this.applicationContextClass;
		if (contextClass == null) {
    
    
			try {
    
    
        // <1> 决定实例化的上下文容器类型
				switch (this.webApplicationType) {
    
    
					// <1.1> 如果是servlet类型
					case SERVLET:
						contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);
						break;
					// <1.2> 如果是reactive类型
					case REACTIVE:
						contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
						break;
					// <1.3> 如果是none类型
					default:
						contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
				}
			} catch (ClassNotFoundException ex) {
    
    
			}
		}
		// <2> 对上下文实例化
		return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
	}
}
  • <1>, determine the type of context container to instantiate

    • <1.1>, if it is a servlet type, use AnnotationConfigServletWebServerApplicationContextthe context
    • <1.2>, if it is a reactive type, use AnnotationConfigReactiveWebServerApplicationContextthe context
    • <1.3>, if it is none type, use AnnotationConfigApplicationContextthe context
  • <2>, instantiate contextClassthe context's and return

3. run method

This is the main process of Spring Boot startup. It looks very long, but there are actually a few key methods. code show as below:

public class SpringApplication {
    
    
	public ConfigurableApplicationContext run(String... args) {
    
    
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();
		// <1> 实例化 SpringApplicationRunListener
		SpringApplicationRunListeners listeners = getRunListeners(args);
		// <2> 触发 ApplicationStartedEvent 事件
		listeners.starting();
		try {
    
    
			// <3> 【 prepareContext 前的基本准备工作】,有命令行参数、准备环境environment、banner图、创建容器等
			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);

			// <4> 【prepareContext 方法(重点)】。单独介绍
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);

			// <5> 【refreshContext 方法(重点)】。单独介绍
			refreshContext(context);

			afterRefresh(context, applicationArguments);
			stopWatch.stop();
			// <6> 触发了核心事件:ApplicationStartedEvent(应用启动好了事件)
			listeners.started(context);
			// <7> 调用了 ApplicationRunner、CommandLineRunner 接口
			callRunners(context, applicationArguments);
		} catch (Throwable ex) {
    
    
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {
    
    
			// <8> 触发了核心事件:ApplicationReadyEvent(应用准备好了事件)
			listeners.running(context);
		} catch (Throwable ex) {
    
    
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}
}
  • Basic preparations before prepareContext
    • <1>At the place, the SpringApplicationRunListener is loaded. The function of this interface is to broadcast the event to the listening Listener after a specific event occurs.
    • <2>, the core event is triggered: ApplicationStartingEvetnt (application startup event)
    • <3>There are command line parameters, environment preparation environment, banner diagram, container creation, etc.
  • prepareContext method (key point) , see『prepareContext
  • refreshContext method (emphasis) , see『prepareContext
  • RefreshContext follow-up improvement work
    • <6>, trigger the event ApplicationStartedEvent
    • <7>At the place, the ApplicationRunner and CommandLineRunner interfaces are called
    • <8>, the event ApplicationReadyEvent is triggered

3.1 prepareContext method (emphasis)

The function of this method is to prepare everything before Applicationexecuting refresh. The point is to execute applyInitializersthe method and trigger several key events of Spring Boot, the code is as follows:

public class SpringApplication {
    
    

	private void prepareContext(ConfigurableApplicationContext context,
	                            ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
	                            ApplicationArguments applicationArguments, Banner printedBanner) {
    
    
		context.setEnvironment(environment);
		postProcessApplicationContext(context);
		// <1> 核心方法:应用 ApplicationContextInitializer 接口
		applyInitializers(context);
		// <2> 核心方法:该版本没有触发事件,但是后续的 Spring Boot版本触发了 ApplicationContextInitializedEvent 事件
		listeners.contextPrepared(context);

		// <3> 向容器中注册2个特殊bean
		context.getBeanFactory().registerSingleton("springApplicationArguments",
				applicationArguments);
		if (printedBanner != null) {
    
    
			context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
		}

		// <4> 加载所有配置源
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		load(context, sources.toArray(new Object[0]));
		// <5> 核心方法:触发 ApplicationPreparedEvent(应用准备好了事件) 事件
		listeners.contextLoaded(context);
	}
}
  • <1>, ApplicationContextInitializerthe interface , which is an important extension point of Spring Boot, and many functions of Spring Cloud are completed through this interface

  • <2>, which is not triggered at 2.0.2.RELEASEany . But the author saw that a subsequent version of Spring Boot triggered an ApplicationContextInitializedEventevent

  • <3>At , two special beans are injected into the container for our subsequent use. These two beans are springApplicationArguments,springBootBanner

  • <4>, use BeanDefinitionLoader to load the Bean definition in the configuration

  • <5>, ApplicationPreparedEventthe event

    public void contextLoaded(ConfigurableApplicationContext context) {
           
           
        // <1>
        for (ApplicationListener<?> listener : this.application.getListeners()) {
           
           
            if (listener instanceof ApplicationContextAware) {
           
           
                ((ApplicationContextAware) listener).setApplicationContext(context);
            }
            context.addApplicationListener(listener);
        }
        // <2>
        this.initialMulticaster.multicastEvent(
            new ApplicationPreparedEvent(this.application, this.args, context));
    }
    
    • <1>At the place, a very important thing has been completed, adding the events defined in the Spring Boot configuration to the context , and subsequent contexts can also trigger configured events
    • <2>, the ApplicationPreparedEvent event is triggered

3.2 refreshContext method (emphasis)

The function of this method is to refresh the container. In fact, it is the core method of ApplicationContext. From here, we can also see that Spring Boot is an extension of Spring rather than a replacement. It does not belong to the
category of Spring Boot in terms of content, so I put the content introduction in
ApplicationContext container start
"

Portal: nanny Spring5 source code analysis

Welcome to exchange technology and work life with the author

contact author

Guess you like

Origin blog.csdn.net/yuchangyuan5237/article/details/128653091