1.前言 深入學習springboot筆記系列,可能會有錯誤還請指正,互相勉勵,互相學習。 SpringBoot 項目啟動只需啟動 主類的 main 函數即可啟動java服務,相比於以往的部署java服務簡化方便了很多,接下我們從主函數入手一步一步剖析源碼是如何通過main函數啟動服務的。 2.Sp ...
1.前言
深入學習springboot筆記系列,可能會有錯誤還請指正,互相勉勵,互相學習。
SpringBoot 項目啟動只需啟動 主類的 main 函數即可啟動java服務,相比於以往的部署java服務簡化方便了很多,接下我們從主函數入手一步一步剖析源碼是如何通過main函數啟動服務的。
2.SpringBoot 項目程式入口
主函數通過一個靜態 run 方法完成整個服務的構建。
@SpringBootApplication
public class LogicalApplication
{
public static void main(String[] args)
{
SpringApplication.run(LogicalApplication.class, args);
}
}
接下來看看靜態的 run 方法的內部實現。
2.1.run 方法構造
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);
}
以上通過 new SpringApplication(primarySources) 執行了初始化的一些相關操作。
3.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));
// 推斷服務類型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 初始化 META-INF/spring.factories 中 所有的 BootstrapRegistryInitializer 類
this.bootstrapRegistryInitializers = new ArrayList<>(getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
// 初始化 META-INF/spring.factories 中 所有的 ApplicationContextInitializer 類
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 初始化 META-INF/spring.factories 中 所有的 ApplicationListener 類
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 推斷主類
this.mainApplicationClass = deduceMainApplicationClass();
}
SpringApplication 初始化主要初始化了 resourceLoader、primarySources、webApplicationType 、bootstrapRegistryInitializers、initializers、listeners、mainApplicationClass 這幾個對象。
其中resourceLoader 預設為null, primarySources 則為主類的有序去重集合。
3.1.webApplicationType 推斷當前項目啟動類型
private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };
private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";
private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";
private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
static WebApplicationType deduceFromClasspath() {
//webflux 響應式
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
// 是否web 項目
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
此處使用 ClassUtils.isPresent(String className, @Nullable ClassLoader classLoader) 方法為內部調用類載入器載入相應的類,如果未找到則拋出ClassNotFoundException異常。類載入 WEBMVC_INDICATOR_CLASS、WEBFLUX_INDICATOR_CLASS 、JERSEY_INDICATOR_CLASS 幾種不同的 Servlet,如果未載入到則為false,從而推斷項目啟動類型。此處我們是web項目,因此最後的返回值是WebApplicationType.SERVLET。
3.2.bootstrapRegistryInitializers 初始化
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) {
//獲取預設類載入器AppClassLoader
ClassLoader classLoader = getClassLoader();
//使用預設類載入器載入META-INFO/spring.factories 中配置的類
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//使用反射將上一步 類載入完成的 names中的類實例化
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
// 將實例化的類排序 排序規則:繼承了 PriorityOrdered 介面的 優先順序最高, 其次實現Ordered 介面 或者添加@Order 註解 通過比較order值的大小來排序,值越小優先順序越高。
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
@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;
}
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
// 緩存cache 中查找
Map<String, List<String>> result = cache.get(classLoader);
if (result != null) {
return result;
}
result = new HashMap<>();
try {
// 此處使用類載入器讀取所有依賴的包中 MEAT-INFO/spring.factories 中配置的類,並添加在緩存cache中
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;
}
以上方法通過預設的類載入AppClassLoader 載入依賴包中 META-INFO/spring.factories 路徑中所有配置的類並緩存在cache中,然後由class 類型找出緩存cache 中需要相應載入的類並通過反射將類實例化併排序返回。
3.3.initializers 、listeners 初始化
同 bootstrapRegistryInitializers 載入流程一致,通過cache 緩存提高載入速度。
3.4.mainApplicationClass 初始化
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的構造器primarySources 是數組類型,因此無法直接通過primarySource 來判斷main方法屬於哪個class。
至此 SpringApplication類初始化載入完成。
總結
1.通過類載入器載入依賴的servlet判斷項目類型;
2.通過類載入器載入指定路徑文件 MEAT-INFO/spring.factories 讀取指定配置文件並使用cache 緩存提高載入速度;
3.通過java反射的方式將指定的 BootstrapRegistryInitializer.class、ApplicationContextInitializer.class、ApplicationListener.class 類的實現類實例化;
4.通過運行異常的堆棧信息推斷main方法所在的類為主類。
後續
下一章 將繼續 講解 SpringBoot 後續啟動流程,謝謝觀看。