SpringBoot启动流程之(构造SpringApplication.class)
- new SpringApplication()流程图如下
1.程序入口
//复合注解,内部包含了组件扫描以及自动配置 @SpringBootApplication public class DemoApplication { public static void main(String[] args) { //引导启动应用 SpringApplication.run(DemoApplication.class, args); } }
2.内部应用启动SpringApplication#Run方法
public static ConfigurableApplicationContext run(Class<?>[]primarySources,String[]args){ //首先new了一个SpringApplication,并且把当前启动类作为入参 return new SpringApplication(primarySources).run(args); }
3.第一大步SpringApplication的构造方法
public SpringApplication(ResourceLoader resourceLoader,Class<?>...primarySources){ this.resourceLoader=resourceLoader; //引导类不能为空 Assert.notNull(primarySources,"PrimarySources must not be null"); this.primarySources=new LinkedHashSet<>(Arrays.asList(primarySources)); //获取当前应用的类型,分为三种NONE,SERVLET,REACTIVE; this.webApplicationType=WebApplicationType.deduceFromClasspath(); //设置了Spring应用上下文初始化的实例 // 获取spring.factories中前缀为org.springframework.context.ApplicationContextInitializer的集合 //并且将ApplicationContextInitializer应用于Spring ApplicationContext setInitializers((Collection)getSpringFactoriesInstances(ApplicationContextInitializer.class)); //设置了Spring上下文所需要的侦听者 //获取spring.factories中前缀为org.springframework.context.ApplicationListener的集合 setListeners((Collection)getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass=deduceMainApplicationClass(); }
3.1. 判断应用类型
static WebApplicationType deduceFromClasspath(){ //根据应用下是否存在某个特定的类来确定确定应用类型 if(ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS,null)&&!ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS,null) &&!ClassUtils.isPresent(JERSEY_INDICATOR_CLASS,null)){ //响应式web容器 return WebApplicationType.REACTIVE; } for(String className:SERVLET_INDICATOR_CLASSES){ if(!ClassUtils.isPresent(className,null)){ //非web容器 return WebApplicationType.NONE; } } //servletWeb容器 return WebApplicationType.SERVLET; }
- 总结
- 根据是否存在某个特定类,来判定当前应用类型
- 应用类型分为三种
- ServletWeb服务器
- 响应式Web服务器
- 非Web服务器
3.2.实例化ApplicationContextInitializer
private<T> Collection<T> getSpringFactoriesInstances(Class<T> type,Class<?>[]parameterTypes,Object...args){ ClassLoader classLoader=getClassLoader(); //根据条件加载spring.factories的实现类,并进行Set集合去重(条件:ApplicationContextInitializer) Set<String> names=new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type,classLoader)); //通过反射创建符合ApplicationContextInitializer接口的对应实例 List<T> instances=createSpringFactoriesInstances(type,parameterTypes,classLoader,args,names); //实例集合排序(留个疑惑)😕 AnnotationAwareOrderComparator.sort(instances); return instances; }
3.2.1 SpringFactoriesLoader#loadFactoryNames
public static List<String> loadFactoryNames(Class<?> factoryType,@Nullable ClassLoader classLoader){ //获取传入参数的转化为全限定类名,用作筛选map集合的value String factoryTypeName=factoryType.getName(); //loadSpringFactories(classLoader)加载内路径下的META-INF/spring.factories并转化为Map //map集合的getOrDefault方法用key进行对比,如果存在key则返回对应的value,否则返回默认值 return loadSpringFactories(classLoader).getOrDefault(factoryTypeName,Collections.emptyList()); }
3.2.2 SpringFactoriesLoader#loadSpringFactories
- 😄(一定要笑着看完加载META-INF/spring.factories的流程)
private static Map<String, List<String>>loadSpringFactories(@Nullable ClassLoader classLoader){ //从缓存获取当前classLoader的map集合 MultiValueMap<String, String> result=cache.get(classLoader); //如果存在,则使用当前classLoader的map集合 if(result!=null){ return result; } //否则,加载META-INF/spring.factories下的元素,并转化为map try{ // 判断当前classLoader是否为空,如果不为空,则使用当前classLoader获取绝对路径信息,否则使用系统classLoader获取信息 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); //将对应url的键值对信息转为properties格式 Properties properties=PropertiesLoaderUtils.loadProperties(resource); //遍历properties的key for(Map.Entry<?, ?> entry:properties.entrySet()){ String factoryTypeName=((String)entry.getKey()).trim(); //遍历对应key的value,并进行格式转化 for(String factoryImplementationName:StringUtils.commaDelimitedListToStringArray((String)entry.getValue())){ //转入对应map result.add(factoryTypeName,factoryImplementationName.trim()); } } } //map存储配置信息,方便下次取用 cache.put(classLoader,result); return result; } catch(IOException ex){ throw new IllegalArgumentException("Unable to load factories from location ["+ FACTORIES_RESOURCE_LOCATION+"]",ex); } }
- 这就是前面提到的META-INF/spring.factories文件的庐山真面目(PS:源文件篇幅太长,我删了点东西👀️ )
- spring.factories这个文件声明了一系列bean的全类名(因为项目就不可能扫描全部电脑文件,而一些依赖不存在于项目能够扫描的范围,所有需要一个文件来保存它们的信息) ,要说SpringBoot最核心的东东是啥,我觉得是这个东西。
- SpringBoot最核心的便是自动装配,而自动装配依赖于META-INF/spring.factories下声明的依赖
- META-INF/spring.factories不止一个,在不同的包下面有多个(具体加载了哪个包下面的,需要自己DEBUG查看)
# Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\ org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener # Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.boot.autoconfigure.BackgroundPreinitializer # Auto Configuration Import Listeners org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\ org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener # Auto Configuration Import Filters org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\ org.springframework.boot.autoconfigure.condition.OnBeanCondition,\ org.springframework.boot.autoconfigure.condition.OnClassCondition,\ org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ # Failure analyzers org.springframework.boot.diagnostics.FailureAnalyzer=\ # Template availability providers org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=\
3.2.3 (开始真正的创建对象喽!)createSpringFactoriesInstances(type,parameterTypes,classLoader,args,names)
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); //通过反射构造特定对象,parameterTypes为参数 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; }
- 好了,到此为止,SpringBoot的初始构造方法的流程基本上都在这了,接下来会缕一缕new SpringApplication(primarySources).run(args)中的run(args)那部分了🚀️
- 其实SpringApplication的构造方法,主要就是完成了这几件事
- 判断了当前应用的类型
- 加载、实例化了SpringApplication的Initializers所需要的类,并且set进SpringApplication
- 加载、实例化了SpringApplication的Listeners所需要的类,并且set进SpringApplication
- 通过运行栈获取了main方法的定义类(这个获取在后面没啥子大事要它干)
PS:前面讲了new SpringApplication,而入口函数就是由new SpringApplication以及run()组成的,下面将来讲一下run()的逻辑🎉️