SpringBoot(1)—啟動原理之SpringApplication對象的創建


創建SpringApplication對象

SpringBoot版本為 2.1.1.RELEASE

@SpringBootApplication
public class SpringbootDemoApplication {

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

這是一個SpringBoot項目的啟動類,在主方法內,調用了SpringApplication類的靜態run( )方法,並且把啟動類的字節碼和主方法的參數作為函數入參。

我們以Debug方式來運行代碼,可以發現SpringBoot項目在啟動時會先創建SpringApplication對象。具體代碼如下:

public static ConfigurableApplicationContext run(Class<?> primarySource,
			String... args) {
		return run(new Class<?>[] { primarySource }, args);
	}


public static ConfigurableApplicationContext run(Class<?>[] primarySources,
			String[] args) {
		return new SpringApplication(primarySources).run(args);
	}


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

可以發現,其實在創建SpringApplcation對象時就是把啟動類的Class類作為入參調用了SpringApplication類的構造方法。

下面將會重點說明一下這個構造方法:

A: this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
這行代碼是將啟動類存儲到SpringApplicationd對象的primarySources 屬性中
B: this.webApplicationType = WebApplicationType.deduceFromClasspath();
這個方法是推斷Web應用類型

static WebApplicationType deduceFromClasspath() {
		//存在DispatcherHander但是不存在DispatcherServlet時,推斷為WebFlux類型
		
		if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null)
				&& !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
				&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
			return WebApplicationType.REACTIVE;
		}
		// 當Servlet和ConfigurableWebApplicationContext均不存在時,推斷為非Web應用
		for (String className : SERVLET_INDICATOR_CLASSES) {
			if (!ClassUtils.isPresent(className, null)) {
				return WebApplicationType.NONE;
			}
		}
		return WebApplicationType.SERVLET;
	}

由於我們只引入了web依賴,所以返回為Servlet

C: setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));

這個方法是用來加載Spring應用上下文初始化器的。

我們debug進入getSpringFactoriesInstances() 中:

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
		return getSpringFactoriesInstances(type, new Class<?>[] {});
	}

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

其實這個方法就是通過SpringFactoriesLoader類的loadFactoryNames()方法從類路徑下的 "META-INF/spring.factories" 文件中找到所有的 "ApplicationContextInitializer",然后保存到 SpringApplication的initializers屬性中。

在下圖中,我們可以看到通過getSpringFactoriesInstances()方法,initializers屬性中保存了6個初始化器

D: setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
和上個方法類似,只不過這個方法是加載Spring應用事件監聽器,但是原理是 一樣的,都是利用SpringFactoriesLoader類的loadFactoryNames()方法,只不過這個初始化的對象變成了ApplicationListener

E: this.mainApplicationClass = deduceMainApplicationClass();
這個方法的作用是從多個配置類中找到含有main方法的主配置類

其實這個方法設計的並不嚴謹,因為在SpringApplication.run(XXX.class)方法中,這個class對象並一定要傳入其主配置類,因為我們知道@SpringBootApplication 元標注 @EnableAutoConfiguration,它們的作用基本相同,換言之,這里的class 對象我們可以傳入任意標注了@EnableAutoConfiguration、@Configuration兩個注解的類。

下一篇文章將會講解SpringApplicaiton中run()方法的運行。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM