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)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 大数据篇:三大指标

    大数据篇:三大指标大数据篇:三大指标上一篇文章中文章讲了如何用服务等级协议(SLA)来评估我们的系统,并讲解了几个常用的SLA指标今天我们来讲分布式系统中另外几个基本概念可扩展性(Scalability)先从我们为什么需要分布式系统说起。原因是我们系统的数据量越来越大,从原来的GB到TB到现在的PB级,单机已经无法胜任这样的工作了。工作中也常有这样的场景,随着业务变得原来越复杂,之前设计的系统无法处理日渐…

    2022年5月10日
    48
  • Flask 框架简单入门

    Flask 框架简单入门Flask框架介绍Flask框架什么是Flask?flask是一种基于python,并且依赖于Jinja2模板引擎(提供网页解析)和WerkzeugWSGI服务(pythonweb应用与web服务之间的接口)的一种微型框架。其中:Jinja2模板引擎:通俗来讲就是服务器接收到用户请求之后,将数据传入HTML文件中后,经过模板引擎的渲染将其呈现在网页中响应给用户。WerkzeugWSGI:pythonweb应用程序是一个被调用的对象,它无法直接与web服务器直接建立联系,所以WS

    2022年9月13日
    4
  • Win10禁止更新设备驱动的几种方式「建议收藏」

    Win10禁止更新设备驱动的几种方式「建议收藏」1、开始菜单选择运行并输入gpedit.msc打开本地组策略编辑器依次展开计算机配置→管理模板→Windows组件→Windows更新;找到并双击打开Windows更新不包括驱动程序;将未配置或者已禁用改成已启用,然后确定保存即可;右键点击开始菜单选择运行并输入RegEdit或者Cortana输入regedit进行搜索;进入注册表后依次展开:HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Wind

    2022年6月4日
    43
  • 贴片电阻基本知识_贴片电阻怎么测试

    贴片电阻基本知识_贴片电阻怎么测试点点滴滴皆是学问;”1故事的起因2走近贴片电阻2.1贴片电阻的优点2.2识别贴片电阻3命名规则总结1故事的起因有一天,小张正在焊接电路板,我拿起旁边的一块板子问起来,这个R0…

    2022年8月21日
    8
  • SpringMVC日期格式化

    SpringMVC日期格式化一、关于SpringMVC日期的格式化大概可分为四点1.@ResponseBody方式返回json的日期格式化2.ajax方式返回json的日期格式化3.数据保存时String转Date4.页面展示时,Date转固定格式的String二、配置实现日期格式化1.@ResponseBody方式返回json的日期格式化配置…

    2022年6月7日
    116
  • 设计模式奠基石——UML关系转化为代码

    设计模式奠基石——UML关系转化为代码

    2021年11月16日
    44

发表回复

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

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