Springboot源碼分析 SpringApplication初始化

Springboot源碼分析 SpringApplication初始化

描述:衆所周知,springboot以輕量級/簡化著稱,其可通過Main方法直接運行項目

SpringApplication初始化過程如下

@SpringBootApplication
public class SpringBootDemo81Application {

	public static void main(String[] args) {//步驟1
		SpringApplication.run(SpringBootDemo81Application.class, args);
	}
}

    /**
	 * Static helper that can be used to run a {@link SpringApplication} from the
	 * specified source using default settings.
	 * @param primarySource the primary source to load
	 * @param args the application arguments (usually passed from a Java main method)
	 * @return the running {@link ApplicationContext}
	 */
	public static ConfigurableApplicationContext run(Class<?> primarySource,
			String... args) {//步驟2
		return run(new Class<?>[] { primarySource }, args);
	}
	
/**
 * Static helper that can be used to run a {@link SpringApplication} from the
 * specified sources using default settings and user supplied arguments.
 * @param primarySources the primary sources to load
 * @param args the application arguments (usually passed from a Java main method)
 * @return the running {@link ApplicationContext}
 */
public static ConfigurableApplicationContext run(Class<?>[] primarySources,
		String[] args) {//步驟3
	return new SpringApplication(primarySources).run(args);
}

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

//步驟5
@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));//@1
	this.webApplicationType = WebApplicationType.deduceFromClasspath();//@2
	setInitializers((Collection) getSpringFactoriesInstances(
			ApplicationContextInitializer.class));//@3
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));//@4
	this.mainApplicationClass = deduceMainApplicationClass();//@5
}

如上所需,通過debug源碼跟進發現總共執行5個步驟
步驟1 傳入SpringBootDemo81Application類,此類是核心應用類,用於構建,args未傳入
步驟2 主要是構建SpringApplication 對象,然後調用其run方法
步驟3.4。.5用於構建SpringApplication 對象,詳細跟分析步驟5如下
@1設置primarySources 等於傳入的Springboot類
@2接着設置當前運行的環境,通過一個枚舉判斷當前所處的環境狀態

/**
 * The application should not run as a web application and should not start an
 * embedded web server.
 */
NONE,

/**
 * The application should run as a servlet-based web application and should start an
 * embedded servlet web server.
 */
SERVLET,

/**
 * The application should run as a reactive web application and should start an
 * embedded reactive web server.
 */
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;
	}

@3初始化加載一些參數和配置

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
			Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = getClassLoader();//@3-1
		// Use names and ensure unique to protect against duplicates
		Set<String> names = new LinkedHashSet<>(//@3-2
				SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
				classLoader, args, names);//@3-3
		AnnotationAwareOrderComparator.sort(instances);//@3-4
		return instances;
	}

//@3-1獲取當前線程的ClassLoader
//@3-2加載配置如下

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 factoryClassName = ((String) entry.getKey()).trim();
					for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
						result.add(factoryClassName, factoryName.trim());
					}
				}
			}
			cache.put(classLoader, result);
			return result;
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	}

這段代碼主要是通過classLoader加載所以spring相關jar裏面META-INF/spring.factories的配置,涉及到工程如下(spring-boot、spring-boot-autoconfigure、spring-boot-devtools)
然後把當前線程的ClassLoader對應資源集合存儲到cache緩存中,便於後續使用

接着分析上面//@3-3

@SuppressWarnings("unchecked")
	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;
	}

之前步驟已經把相關類加載,此處主要是轉換實體,加入到集合中
//@3-4對集合進行排序
//@4同3一樣進行賦值並加入集合
//@5 獲取到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;
	}

獲取當前方法的調用棧,找到main函數所在類,並設置到SpringApplication對象的mainApplicationClass屬性

總結:

SpringApplication初始化,主要進行了項目包結構配置加載,並獲取當前方法的調用棧,找到main函數所在類所運行

作者簡介:張程 技術研究

更多文章請關注微信公衆號:zachary分解獅 (frankly0423)

公衆號

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章