springboot启动核心源码分析
new SpringApplication(springboot2.class).run(args);
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); //dk 根据类路径(所有jar)来推断当前容器的环境,none,servlet,reactive //dk 从下面的代码分析,如果所有jar包下能找到org.springframework.web.reactive.DispatcherHandler=》reactive //dk存在javax.servlet.Servlet","org.springframework.web.context.ConfigurableWebApplicationContext" //则为servlet,否则就是none this.webApplicationType = WebApplicationType.deduceFromClasspath(); //dk 加载所有jar路径下的META-INF/spring.factories配置文件,具体分析如下 //dk 同时将ApplicationContextInitializer类型对应的value组成一个集合,7个初始器 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); //dk 设置监听器,listeners有值了,11个监听器 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); //dk 推断出当前主函数 this.mainApplicationClass = deduceMainApplicationClass(); }
推断的具体代码
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; }
注意:spring-core封装的方法
ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null)
从47种基础类型判断当前class是否是其中的一种 clazz = commonClassCache.get(name);
从类路径获取当前的class,如果加载不到jvm则直接报错 return Class.forName(name, false, clToUse);
加载所有jar下的META-INF/spring.factories配置文件,并将其key-value放入properties
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = getClassLoader(); // Use names and ensure unique to protect against duplicates //dk set后面的参数很关键, Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); //dk 将ApplicationContextInitializer对应的value进行初始化 List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); //dk 进行排序,initializers有值了 AnnotationAwareOrderComparator.sort(instances); return instances; }
解析的核心代码
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); //dk 读取当前资源文件下的所以属性,一般有Initializers,Listeners,Import Listeners // Import Filters,EnableAutoConfiguration,Failure analyzers,providers Properties properties = PropertiesLoaderUtils.loadProperties(resource); //dk 注意2级for循环结束后,代表一个文件资源的所有key和value已经存放到result,当while结束后 //dk 代表整个classpath路径下的文件都遍历完了,放到一个cache里面 for (Map.Entry<?, ?> entry : properties.entrySet()) { //dk 获取7大key String factoryTypeName = ((String) entry.getKey()).trim(); //dk 分别将7大key对应的value放入result,注意result可以有多个,所以要循环来处理 for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) { result.add(factoryTypeName, factoryImplementationName.trim()); } } } cache.put(classLoader, result);
先初始化所有类路径下的的Initializer
spring-boot-autoconfigure下的配置
# Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\ org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
spring-boot下的配置
# Application Context Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\ org.springframework.boot.context.ContextIdApplicationContextInitializer,\ org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\ org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\ org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
推断出主函数
private Class<?> deduceMainApplicationClass() { try { //dk 获取当前栈信息 StackTraceElement[] stackTrace = new RuntimeException().getStackTrace(); for (StackTraceElement stackTraceElement : stackTrace) { if ("main".equals(stackTraceElement.getMethodName())) { return Class.forName(stackTraceElement.getClassName()); } } }