newSpringApplication

简介: newSpringApplication

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的构造方法,主要就是完成了这几件事
  1. 判断了当前应用的类型
  2. 加载、实例化了SpringApplication的Initializers所需要的类,并且set进SpringApplication
  3. 加载、实例化了SpringApplication的Listeners所需要的类,并且set进SpringApplication
  4. 通过运行栈获取了main方法的定义类(这个获取在后面没啥子大事要它干)

PS:前面讲了new SpringApplication,而入口函数就是由new SpringApplication以及run()组成的,下面将来讲一下run()的逻辑🎉️

目录
相关文章
|
20天前
|
运维 Cloud Native Devops
一线实战:运维人少,我们从 0 到 1 实践 DevOps 和云原生
上海经证科技有限公司为有效推进软件项目管理和开发工作,选择了阿里云云效作为 DevOps 解决方案。通过云效,实现了从 0 开始,到现在近百个微服务、数百条流水线与应用交付的全面覆盖,有效支撑了敏捷开发流程。
19228 24
|
22天前
|
人工智能 自然语言处理 搜索推荐
阿里云Elasticsearch AI搜索实践
本文介绍了阿里云 Elasticsearch 在AI 搜索方面的技术实践与探索。
18776 15
|
20天前
|
Rust Apache 对象存储
Apache Paimon V0.9最新进展
Apache Paimon V0.9 版本即将发布,此版本带来了多项新特性并解决了关键挑战。Paimon自2022年从Flink社区诞生以来迅速成长,已成为Apache顶级项目,并广泛应用于阿里集团内外的多家企业。
17476 10
Apache Paimon V0.9最新进展
|
23天前
|
存储 人工智能 前端开发
AI 网关零代码解决 AI 幻觉问题
本文主要介绍了 AI Agent 的背景,概念,探讨了 AI Agent 网关插件的使用方法,效果以及实现原理。
18651 14
|
20天前
|
人工智能 自然语言处理 搜索推荐
评测:AI客服接入钉钉与微信的对比分析
【8月更文第22天】随着人工智能技术的发展,越来越多的企业开始尝试将AI客服集成到自己的业务流程中。本文将基于《10分钟构建AI客服并应用到网站、钉钉或微信中》的解决方案,详细评测AI客服在钉钉和微信中的接入流程及实际应用效果,并结合个人体验分享一些心得。
9895 7
|
24天前
|
消息中间件 弹性计算 关系型数据库
函数计算驱动多媒体文件处理解决方案体验评测
从整体解读到部署体验,多方位带你了解如何利用函数计算驱动多媒体文件处理,告别资源瓶颈。
10429 12
|
15天前
|
存储 JSON Serverless
西游再现,函数计算一键部署 Flux 超写实文生图模型部署
参与体验活动生成西游人物图像,既有机会赢取好礼!本次实验在函数计算中内置了flux.1-dev-fp8大模型,通过函数计算+Serverless应用中心一键部署Flux模型,快速生成超写实图像。首次开通用户可领取免费试用额度,部署过程简单高效。完成部署后,您可以通过修改提示词生成各种风格的图像,体验Flux模型的强大绘图能力。
西游再现,函数计算一键部署 Flux 超写实文生图模型部署
|
27天前
|
SQL 容灾 关系型数据库
让X不断延伸, 从跨AZ到跨Region再到跨Cloud
本文从“空间”这一维度,聊一聊PolarDB-X在跨空间部署能力上的不断发展和延伸,以及在不同空间范围下的高可用和容灾能力,并着重介绍一下最新的产品能力——GDN(Global Database Network)。
|
27天前
|
缓存 测试技术 调度
PolarDB-X的TPC-H列存执行计划
本文从官方的角度逐条解析PolarDB-X在TPC-H列存执行计划的设计要点。这些要点不仅包含了各项优化的原理,还提供了相关的证明与代码实现,希望帮助读者更深入地理解PolarDB-X的列存优化器。
|
28天前
|
人工智能 数据可视化 定位技术
DataV AI助手小技巧-如何制作PPT数据地图
“数据地图”是PPT汇报地区业务数据的最佳形式之一;以往制作数据地图需要用户有一定的编程和数据处理基础,制作门槛较高;随着DataV整合通义千问大模型能力之后,不懂编程和设计的用户也可以借助AI助手“零代码”制作数据地图,真正实现了人人可用的地图数据可视化。 进入大模型AI时代,人人可以变成职场跨界多面手!
11098 2
DataV AI助手小技巧-如何制作PPT数据地图