springboot项目启动原理_转膜原理

springboot项目启动原理_转膜原理1.总览上图为SpringBoot启动结构图,我们发现启动流程主要分为三个部分,第一部分进行SpringApplication的初始化模块,配置一些基本的环境变量、资源、构造器、监听器,第二部分实现了应用具体的启动方案,包括启动流程的监听模块、加载配置环境模块、及核心的创建上下文环境模块,第三部分是自动化配置模块,该模块作为springboot自动配置核心2.常用注解解释任何一个标注了@Bean的方法,其返回值将作为一个bean定义注册到Spring的IoC容器,方法名将默认成该bean定义的id.

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

在这里插入图片描述

1.总览

上图为SpringBoot启动结构图,我们发现启动流程主要分为三个部分,第一部分进行SpringApplication的初始化模块,配置一些基本的环境变量、资源、构造器、监听器,第二部分实现了应用具体的启动方案,包括启动流程的监听模块、加载配置环境模块、及核心的创建上下文环境模块,第三部分是自动化配置模块,该模块作为springboot自动配置核心

2.常用注解解释

  • 任何一个标注了@Bean的方法,其返回值将作为一个bean定义注册到Spring的IoC容器,方法名将默认成该bean定义的id。

  • @SpringBootConfiguration:该注解作用就是将当前的类作为一个JavaConfig,然后触发注解@EnableAutoConfiguration和@ComponentScan的处理,本质上与@Configuration注解没有区别。

  • @ComponentScan,就是自动扫描并加载符合条件的组件(比如@Component和@Repository等)或者bean定义;(所以SpringBoot的启动类最好是放在root package下,因为默认不指定basePackages)

  • @EnableAutoConfiguration,借助@Import的支持,收集和注册特定场景相关的bean定义;也是借助@Import的帮助,将所有符合自动配置条件的bean定义加载到IoC容器
    (1) @AutoConfigurationPackage:通过@Import(AutoConfigurationPackages.Registrar.class)注册当前启动类的根package;注册 org.springframework.boot.autoconfigure.AutoConfigurationPackages 的BeanDefinition
    (2) Import(AutoConfigurationImportSelector.class): “META-INF/spring.factories”
    该方法在springboot启动流程——bean实例化前被执行,返回要实例化的类信息列表;
    如果获取到类信息,spring可以通过类加载器将类加载到jvm中,现在我们已经通过spring-boot的starter依赖方式依赖了我们需要的组件,那么这些组件的类信息在select方法中就可以被获取到。将所有符合条件(spring.factories)的bean定义(如Java Config@Configuration配置)都加载到当前SpringBoot创建并使用的IoC容器。
    总结:从classpath中搜寻所有的META-INF/spring.factories配置文件,并将其中org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的配置项通过反射(Java Refletion)实例化为对应的标注了@Configuration的JavaConfig形式的IoC容器配置类,然后汇总为一个并加载到IoC容器。

    • 通过SpringFactoriesLoader#loadFactoryNames获取应考虑的自动配置名称,例如来源于spring-boot-autoconfigure jar包下的 META-INF/spring.factories 文件下的配置
    • 通过 filter 过滤掉当前环境不需要自动装配的类,各种 @Conditional 不满足就被过滤掉
    • 需要自动装配的全路径类名注册到 SpringIOC 容器,自此 SpringBoot 自动装配完成!
      在这里插入图片描述

3.启动流程概括图

在这里插入图片描述

4.详解AutoConfigurationImportSelector

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
  ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered { 
   
  1. 可以看到实现了 DeferredImportSelector接口,该接口继承自ImportSelector,根据Javadoc可知,多用于导入被@Conditional注解的Bean
  2. ImportSelector接口中有个selectImports方法,SpringBoot启动时会调用该方法,进行自动装配的处理
@Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) { 
   
        if (!isEnabled(annotationMetadata)) { 
   
            return NO_IMPORTS;
        }
        AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
                .loadMetadata(this.beanClassLoader);
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
        List<String> configurations = getCandidateConfigurations(annotationMetadata,
                attributes);
        configurations = removeDuplicates(configurations);
        Set<String> exclusions = getExclusions(annotationMetadata, attributes);
        checkExcludedClasses(configurations, exclusions);
        configurations.removeAll(exclusions);
        configurations = filter(configurations, autoConfigurationMetadata);
        fireAutoConfigurationImportEvents(configurations, exclusions);
        return StringUtils.toStringArray(configurations);
    }
  • getCandidateConfigurations(annotationMetadata,attributes);其实是去加载各个组件jar下的”META-INF/spring.factories”;外部文件。
  • 该方法在springboot启动流程——bean实例化前被执行,返回要实例化的类信息列表;
  • 如果获取到类信息,spring可以通过类加载器将类加载到jvm中,现在我们已经通过spring-boot的starter依赖方式依赖了我们需要的组件,那么这些组件的类信息在select方法中就可以被获取到。
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { 
   
 List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
 Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
 return configurations;
 }
  1. 其返回一个自动配置类的类名列表,方法调用了loadFactoryNames方法,查看该方法
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) { 
   
 String factoryClassName = factoryClass.getName();
 return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
 }

自动配置器会跟根据传入的factoryClass.getName()到项目系统路径下所有的spring.factories文件中找到相应的key,从而加载里面的类。

其中,最关键的要属@Import(AutoConfigurationImportSelector.class),借助AutoConfigurationImportSelector,@EnableAutoConfiguration可以帮助SpringBoot应用将所有符合条件(spring.factories)的bean定义(如Java Config@Configuration配置)都加载到当前SpringBoot创建并使用的IoC容器。

SpringFactoriesLoader为Spring工厂加载器,该对象提供了loadFactoryNames方法,入参为factoryClass和classLoader即需要传入工厂类名称和对应的类加载器,方法会根据指定的classLoader,加载该类加器搜索路径下的指定文件,即spring.factories文件;

5.总结SpringBoot的自动装配

    //这个方法重写至ImportSelect
	@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) { 
   
		if (!isEnabled(annotationMetadata)) { 
   
			return NO_IMPORTS;
		}
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}
	protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) { 
   
		if (!isEnabled(annotationMetadata)) { 
   
			return EMPTY_ENTRY;
		}
		// 获取预先定义的应考虑的自动配置类名称
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
		configurations = removeDuplicates(configurations);
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		checkExcludedClasses(configurations, exclusions);
		configurations.removeAll(exclusions);
		// 通过filter过滤掉当前环境不需要自动装配的类,比如没有集成RabbitMQ,就不需要,或者有的条件@Conditional不满足也不需要自动装配
		configurations = getConfigurationClassFilter().filter(configurations);
		fireAutoConfigurationImportEvents(configurations, exclusions);
		// 返回需要自动装配的全路径类名
		return new AutoConfigurationEntry(configurations, exclusions);
	}
	protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { 
   
		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
				getBeanClassLoader());
		Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
				+ "are using a custom packaging, make sure that file is correct.");
		return configurations;
	}
  • 通过SpringFactoriesLoader#loadFactoryNames获取应考虑的自动配置名称,通过META-INF/spring.factories下的配置
  • 执行完configurations =
    getConfigurationClassFilter().filter(configurations);之后,各种@Conditional不满足就被过滤掉,剩下35个了
  • SpringFactoriesLoader为Spring工厂加载器,该对象提供了loadFactoryNames方法,入参为factoryClass和classLoader即需要传入工厂类名称和对应的类加载器,方法会根据指定的classLoader,加载该类加器搜索路径下的指定文件,即spring.factories文件;
    public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) { 
   
        ClassLoader classLoaderToUse = classLoader;
        if (classLoader == null) { 
   
            classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
        }

        String factoryTypeName = factoryType.getName();
        return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
    }

在上面的代码可以看到自动配置器会跟根据传入的factoryType.getName()到项目系统路径下所有的spring.factories文件中找到相应的key,从而加载里面的类。我们就选取这个mybatis-spring-boot-autoconfigure下的spring.factories文件
在这里插入图片描述
进入org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration中,主要看一下类头:
在这里插入图片描述

  • 发现@Spring的Configuration,俨然是一个通过注解标注的springBean,继续向下看,@ConditionalOnClass({ SqlSessionFactory.class,SqlSessionFactoryBean.class})这个注解的意思是:当存在SqlSessionFactory.class,SqlSessionFactoryBean.class这两个类时才解析MybatisAutoConfiguration配置类,否则不解析这一个配置类,make sence,我们需要mybatis为我们返回会话对象,就必须有会话工厂相关类
  • @CondtionalOnBean(DataSource.class):只有处理已经被声明为bean的dataSource
  • @ConditionalOnMissingBean(MapperFactoryBean.class)这个注解的意思是如果容器中不存在name指定的bean则创建bean注入,否则不执行(该类源码较长,篇幅限制不全粘贴)

以上配置可以保证sqlSessionFactory、sqlSessionTemplate、dataSource等mybatis所需的组件均可被自动配置,@Configuration注解已经提供了Spring的上下文环境,所以以上组件的配置方式与Spring启动时通过mybatis.xml文件进行配置起到一个效果。通过分析我们可以发现,只要一个基于SpringBoot项目的类路径下存在SqlSessionFactory.class, SqlSessionFactoryBean.class,并且容器中已经注册了dataSourceBean,就可以触发自动化配置,意思说我们只要在maven的项目中加入了mybatis所需要的若干依赖,就可以触发自动配置,但引入mybatis原生依赖的话,每集成一个功能都要去修改其自动化配置类,那就得不到开箱即用的效果了。所以Spring-boot为我们提供了统一的starter可以直接配置好相关的类,触发自动配置所需的依赖(mybatis)如下:
在这里插入图片描述

因为maven依赖的传递性,我们只要依赖starter就可以依赖到所有需要自动配置的类,实现开箱即用的功能。也体现出Springboot简化了Spring框架带来的大量XML配置以及复杂的依赖管理,让开发人员可以更加关注业务逻辑的开发。

6.SpringBoot启动流程解析

  1. run方法中去创建了一个SpringApplication实例,在该构造方法内,我们可以发现其调用了一个初始化的initialize方法,主要是为SpringApplication对象赋一些初值
  2. 创建了应用的监听器SpringApplicationRunListeners并开始监听
  3. 加载SpringBoot配置环境(ConfigurableEnvironment),如果是通过web容器发布,会加载StandardEnvironment,其最终也是继承了ConfigurableEnvironment,类图如下
    在这里插入图片描述
    可以看出,*Environment最终都实现了PropertyResolver接口,我们平时通过environment对象获取配置文件中指定Key对应的value方法时,就是调用了propertyResolver接口的getProperty方法
  4. 配置环境(Environment)加入到监听器对象中(SpringApplicationRunListeners)
  5. 创建run方法的返回对象:ConfigurableApplicationContext(应用配置上下文),我们可以看一下创建方法:
    在这里插入图片描述
    方法会先获取显式设置的应用上下文(applicationContextClass),如果不存在,再加载默认的环境配置(通过是否是web environment判断),默认选择AnnotationConfigApplicationContext注解上下文(通过扫描所有注解类来加载bean),最后通过BeanUtils实例化上下文对象,并返回,ConfigurableApplicationContext类图如下:
    在这里插入图片描述
    主要看其继承的两个方向:
    LifeCycle:生命周期类,定义了start启动、stop结束、isRunning是否运行中等生命周期空值方法
    ApplicationContext:应用上下文类,其主要继承了beanFactory(bean的工厂类)
  6. 回到run方法内,prepareContext方法将listeners、environment、applicationArguments、banner等重要组件与上下文对象关联
  7. 接下来的refreshContext(context)方法(初始化方法如下)将是实现spring-boot-starter-*(mybatis、redis等)自动化配置的关键,包括spring.factories的加载,bean的实例化等核心工作。
  8. refresh方法:配置结束后,Springboot做了一些基本的收尾工作,返回了应用环境上下文。回顾整体流程,Springboot的启动,主要创建了配置环境(environment)、事件监听(listeners)、应用上下文(applicationContext),并基于以上条件,在容器中开始实例化我们需要的Bean,至此,通过SpringBoot启动的程序已经构造完成,接下来我们来探讨自动化配置是如何实现。

参考:
SpringBoot启动流程解析
SPRINGBOOT启动原理

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/171557.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • matlab 计算变异系数,变异系数法求权重matlab代码

    matlab 计算变异系数,变异系数法求权重matlab代码《变异系数法求权重matlab代码》由会员分享,可在线阅读,更多相关《变异系数法求权重matlab代码(1页珍藏版)》请在读根文库上搜索。1、变异系数法求权重matlab代码clear;clc;data1,header1=xlsread(statistic1.xlsx,ECO);%必须将statistic.xlsx至于默认文件下,或者给出完整路径data2,header2=xl…

    2022年4月28日
    116
  • intellij idea乱码怎么办(乱码)

    1.首先是编辑器的乱码,这个很好解决,file->settings->appearence里面有个Name设置成支持中文的字体(这个很重要)同样还要再settings中的Eidtor->FileEncodings里面设置字体编码格式,一般都是UTF-8,GBK什么的也行。2.找到idea安装目录bin目录下如下图所示两个文件,用编辑器打开,在文件末尾添加-Dfile.encoding=UTF-

    2022年4月16日
    777
  • 小波阈值去噪

    小波阈值去噪目录1.概念2.原理3.影响降噪效果的因素3.1小波基的选择3.2分解层数的选择3.3阈值的选择3.4阈值函数的选择4.MATLAB代码参考文献1.概念小波分析即用Mallat塔式算法对信号进行降阶分解。该算法在每尺度下将信号分解成近似分量与细节分量。近似分量表示信号的高尺度,即低频信息;细节分量表示信号的低尺度,即高频信息。对含有噪声的信号,噪声分量的主要能量集中在小波分解的细节分量中。2.原理小波阈值去噪的实质为抑制信号中无用部分、增强有用部分的过

    2022年6月16日
    53
  • C++中关于[]静态数组和new分配的动态数组的区别分析

    这篇文章主要介绍了C++中关于[]静态数组和new分配的动态数组的区别分析,很重要的概念,需要的朋友可以参考下本文以实例分析了C++语言中关于[]静态数组和new分配的动态数组的区别,可以帮助大家加

    2021年12月27日
    40
  • bytebuffer.putint_get的用法和例句

    bytebuffer.putint_get的用法和例句最近再看java的NIO,里面提到了几个基本的类,其中ByteBuffer是最基础的,用于Channel的读写传输数据使用。下面总结一下我理解的ByteBuffer。先从代码开始分析staticpublicvoidasIntBuffer(){ByteBufferbBuf=ByteBuffer.allocate(512);bBuf.putI

    2022年10月2日
    3
  • matlab非线性整数优化,fmincon整数优化

    matlab非线性整数优化,fmincon整数优化MATLAB非线性优化fmincon_数学_自然科学_专业资料。精心整理act…注意:[1]fmincon函数提供了大型优化算法和中型优化算法。默认时,若…[x,fval,exitflag]=fmincon(@ff8,x0,[],[],[],[],[],[],nonlcon)四、整数线性规划算法说明:下面给出用分枝定界法求解整数线性规划的M函数文件……fminc…

    2022年5月6日
    48

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号