Spring boot起步依赖、自动配置与启动执行流程原理

什么是 Spring Boot?

Spring Boot :起步依赖 自动配置
Spring Boot 并不是一个框架,它是一种创建独立应用程序的更简单方法,只需要很少或没有配置(相比于 Spring 来说)。Spring Boot最好的特性之一是它利用现有的 Spring 项目和第三方项目来开发适合生产的应用程序。
spring boot来简化spring应用开发,约定大于配置,去繁从简,just run就能创建一个独立的,产品级别的应用,无需打包部署,直接运行

springBoot约定大于配置到底是什么意思?

约定优于配置(convention over configuration),也称作按约定编程,是一种软件设计范式,旨在减少软件开发人员需做决定的数量,获得简单的好处,而又不失灵活性。

在SpringBoot中,约定大于配置可以从以下两个方面来理解:

  • 开发人员仅需规定应用中不符合约定的部分
  • 在没有规定配置的地方,采用默认配置,以力求最简配置为核心思想总的来说,上面两条都遵循了推荐默认配置的思想。当存在特殊需求的时候,自定义配置即可。这样可以大大的减少配置工作,这就是所谓的“约定”。

那么SpringBoot中有哪些约定呢?
✓Maven的目录结构。默认有resources文件夹,存放资源配置文件。src-main-resources,src-main-java。默认的编译生成的类都在targe文件夹下面
✓spring boot默认的配置文件必须是,也只能是application.命名的yml文件或者properties文件,且唯一application.yml中默认属性。
✓数据库连接信息必须是以spring: datasource: 为前缀;多环境配置。该属性可以根据运行环境自动读取不同的配置文件;端口号、请求路径等

Spring Boot 优点非常多,如:
一、独立运行
Spring Boot而且内嵌了各种servlet容器,Tomcat、Jetty等,现在不再需要打成war包部署到容器中,
Spring Boot只要打成一个可执行的jar包就能独立运行,所有的依赖包都在一个jar包内。
二、简化配置
spring-boot-starter-web启动器自动依赖其他组件,简少了maven的配置。
三、自动配置
Spring Boot能根据当前类路径下的类、jar包来自动配置bean,如添加一个spring-boot-starter-web启动器就能拥有web的功能,无需其他配置。
四、无代码生成和XML配置
Spring Boot配置过程中无代码生成,也无需XML配置文件就能完成所有配置工作,这一切都是借助于条件注解完成的,这也是Spring4.x的核心功能之一。
五、应用监控
Spring Boot提供一系列端点可以监控服务及应用,做健康检测。

SpringBoot核心注解是哪个?它主要由哪几个注解组成的?
启动类上面的注解是@SpringBootApplication,它也是 Spring Boot 的核心注解,主要组合包含了以下3 个注解:

  • @SpringBootConfiguration:组合了 @Configuration 注解,实现配置文件的功能。在启动类里面标注了@Configuration,意味着它其实也是一个 IoC容器的配置类.

  • @EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项,如关闭数据源自动配置功能: @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })。springboot 应用把所有符合条件的@Configuration 配置都加载到当前 SpringBoot 创建并使用的 IoC 容器中。

  • @ComponentScan:Spring组件扫描。默认会扫描当前 package 下的的所有加了@Component 、@Repository、@Service、@Controller的类到 IoC 容器中;

Spring Boot、Spring MVC 和 Spring 有什么区别?
1、Spring
Spring最重要的特征是依赖注入。所有 SpringModules 不是依赖注入就是 IOC 控制反转。
当我们恰当的使用 DI 或者是 IOC 的时候,我们可以开发松耦合应用。松耦合应用的单元测试可以很容易的进行。
2、Spring MVC
Spring MVC 提供了一种分离式的方法来开发 Web 应用。通过运用像 DispatcherServelet,MoudlAndView 和 ViewResolver 等一些简单的概念,开发 Web 应用将会变的非常简单。
3、SpringBoot
Spring 和 SpringMVC 的问题在于需要配置大量的参数。
Spring Boot 通过一个自动配置和启动的项来目解决这个问题。为了更快的构建产品就绪应用程序,Spring Boot 提供一些非功能性特征

起步依赖

如何理解 Spring Boot 中的 Starters?起步依赖
Starters是什么:
spring-boot-starter-xxx就是SpringBoot的起步依赖。SpringBoot通过提供众多起步依赖降低项目依赖的复杂度

Starters可以理解为启动器,它包含了一系列可以集成到应用里面的依赖包,你可以一站式集成Spring及其他技术,而不需要到处找示例代码和依赖包。如你想使用Spring JPA访问数据库,只要加入spring-boot-starter-data-jpa启动器依赖就能使用了。Starters包含了许多项目中需要用到的依赖,它们能快速持续的运行,都是一系列得到支持的管理传递性依赖。

Spring Boot Starters 是一系列依赖关系的集合,因为它的存在,项目的依赖之间的关系对我们来说变的更加简单了。举个例子:在没有Spring Boot Starters之前,我们开发REST服务或Web应用程序时; 我们需要使用像Spring MVC,Tomcat和Jackson这样的库,这些依赖我们需要手动一个一个添加。但是,有了 Spring Boot Starters 我们只需要一个只需添加一个spring-boot-starter-web一个依赖就可以了,这个依赖包含的字依赖中包含了我们开发REST 服务需要的所有依赖。

自动装配

自动装配在refresh方法中的 beanfactorpostprocessor
在这里插入图片描述
@SpringBootApplication 注解的原因,我们知道 @SpringBootApplication 看作是 @Configuration、@EnableAutoConfiguration、@ComponentScan 注解的集合。

@EnableAutoConfiguration:启用 SpringBoot 的自动配置机制
@ComponentScan: 扫描被@Component (@Service,@Controller)注解的bean,注解默认会扫描该类所在的包下所有的类。
@Configuration:允许在上下文中注册额外的bean或导入其他配置类

@EnableAutoConfiguration是启动自动配置的关键,源码如下

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

从源码中可以知道,最关键的要属@EnableAutoConfiguration 注解通过Spring 提供的 @Import 注解导入了AutoConfigurationImportSelector类(@Import 注解可以导入配置类或者Bean到当前类中)。
selectImports方法
selectImports 方法最终会调用 SpringFactoriesLoader.loadFactoryNames
方法来获取一个全面的常用 BeanConfiguration 列表

public String[] selectImports(AnnotationMetadata annotationMetadata) {
 if (!this.isEnabled(annotationMetadata)) {
 return NO_IMPORTS;
 } else {
//1 加载 META-INF/spring-autoconfigure-metadata.properties 文件
 AutoConfigurationMetadata autoConfigurationMetadata =
AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
//2 获取注解的属性及其值(PS:注解指的是@EnableAutoConfiguration 注解)
 AnnotationAttributes attributes =
this.getAttributes(annotationMetadata);
//3.在 classpath 下所有的 META-INF/spring.factories 文件中查找
org.springframework.boot.autoconfigure.EnableAutoConfiguration 的值,
并将其封装到一个 List 中返回
 List<String> configurations =
this.getCandidateConfigurations(annotationMetadata, attributes);
//4.对上一步返回的 List 中的元素去重、排序
 configurations = this.removeDuplicates(configurations);
//5.依据第 2 步中获取的属性值排除一些特定的类
 Set<String> exclusions =
this.getExclusions(annotationMetadata, attributes);
//6 对上一步中所得到的 List 进行过滤,过滤的依据是条件匹配。这里用到的
过滤器是
//org.springframework.boot.autoconfigure.condition.OnClassCondition
最终返回的是一个 ConditionOutcome[]
//数组。(PS:很多类都是依赖于其它的类的,当有某个类时才会装配,所以这
次过滤的就是根据是否有某个
//class 进而决定是否装配的。这些类所依赖的类都写在
META-INF/spring-autoconfigure-metadata.properties 文件里)
 this.checkExcludedClasses(configurations, exclusions);
 configurations.removeAll(exclusions);
 configurations = this.filter(configurations,
autoConfigurationMetadata);
 this.fireAutoConfigurationImportEvents(configurations,
exclusions);
 return StringUtils.toStringArray(configurations);
 }
 }
 protected List<String> getCandidateConfigurations(AnnotationMetadata 
metadata, AnnotationAttributes attributes) {
 List<String> configurations =
SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderF
actoryClass(), 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;
 }
...
}

在这里插入图片描述

AutoConfigurationImportSelector类中getCandidateConfigurations方法会将所有自动配置类的信息以 List 的形式返回。这些配置信息会被 Spring 容器作 bean 来管理。同时借助于 Spring 框架原有的一个工具类:
SpringFactoriesLoader,@EnableAutoConfiguration 就可以实现智能的自动配置

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 中加载配置,SpringFactoriesLoader 属于Spring 框架私有的一种扩展方案,其主要功能就是从指定的配置文件META-INF/spring.factories 加载配置,即根据@EnableAutoConfiguration 的完整类名org.springframework.boot.autoconfigure.EnableAutoConfiguration作为查找的 Key,获取对应的一组@Configuration 类

总结:@EnableAutoConfiguration 作用就是从 classpath 中搜寻所有的
META-INF/spring.factories 配置文件,并将其中org.springframework.boot.autoconfigure.EnableutoConfiguration 对应的配置项通过反射(Java Refletion)实例化为对应的标注了@Configuration 的JavaConfig 形式的 IoC 容器配置类,然后汇总为一个并加载到 IoC 容器。这些功能配置类要生效的话,会去 classpath 中找是否有该类的依赖类(也就是pom.xml 必须有对应功能的 jar 包才行)并且配置类里面注入了默认属性值类,功能类可以引用并赋默认值。生成功能类的原则是自定义优先,没有自定义时才会使用自动装配类。

所以功能类能生效需要的条件:(1)spring.factories 里面有这个类的配置类(一个配置类可以创建多个围绕该功能的依赖类)(2)pom.xml里面需要有对应的 jar 包

流程
1.通过各种注解实现了类与类之间的依赖关系,容器在启动的时候
Application.run,会调用 EnableAutoConfigurationImportSelector.class 的
selectImports 方法(其实是其父类的方法)–这里需要注意,调用这个方法之前发生了什么和是在哪里调用这个方法需要进一步的探讨

2.selectImports 方法最终会调用SpringFactoriesLoader.loadFactoryNames方法来获取一个全面的常用 BeanConfiguration 列表

3.loadFactoryNames 方法会读取FACTORIES_RESOURCE_LOCATION(也就是spring-boot-autoconfigure.jar 下面的 spring.factories),获取到所有的Spring 相关的 Bean 的全限定名 ClassName,大概 120 多个

4.selectImports 方法继续调用 filter(configurations, autoConfigurationMetadata);这个时候会根据这些 BeanConfiguration 里面的条件,来一一筛选,最关键的是@ConditionalOnClass,这个条件注解会去 classpath 下查找,jar 包里面是否有这个条件依赖类,所以必须有了相应的 jar 包,才有这些依赖类,才会生成IOC 环境需要的一些默认配置 Bean

5.最后把符合条件的 BeanConfiguration 注入默认的
EnableConfigurationPropertie 类里面的属性值,并且注入到 IOC 环境当中

spring中的spi机制的原理是什么?

SPI的全名为Service Provider Interface,为某个接口寻找服务实现的机制。
当服务的提供者,提供了服务接口的一种实现之后,在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。通过这个约定,就不需要把服务放在代码中了,通过模块被装配的时候就可以发现服务类了。

在springboot的自动装配过程中,最终会加载META-INF/spring.factories文件,而加载的过程是由SpringFactoriesLoader加载的。从CLASSPATH下的每个Jar包中搜寻所有META-INF/spring.factories配置文件,然后将解析properties文件,找到指定名称的配置后返回。需要注意的是,其实这里不仅仅是会去ClassPath路径下查找,会扫描所有路径下的Jar包,只不过这个文件只会在Classpath下的jar包中。

通过spi技术可以自定义starter,在自定义的配置文件META-INF/spring.factories中加入实现类,依赖这个starter的项目就会扫描jar包下的配置,找到实现类进行装载实例化

SpringApplication执行流程

SpringApplication的run方法的实现
run流程图
在这里插入图片描述1) 如果我们使用的是SpringApplication的静态run方法,那么,这个方法里面首先要创建一个SpringApplication对象实例,然后调用这个创建好的SpringApplication的实例方法。在SpringApplication实例初始化的时候,它会提前做几件事情:

  • 根据classpath里面是否存在某个特征类(org.springframework.web.context.ConfigurableWebApplicationContext)来决定是否应该创建一个为Web应用使用的ApplicationContext类型。
  • 使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationContextInitializer。
  • 使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationListener。
  • 推断并设置main方法的定义类。

2) SpringApplication实例初始化完成并且完成设置后,就开始执行run方法的逻辑了,方法执行伊始,首先遍历执行所有通SpringFactoriesLoader可以查找到并加载的SpringApplicationRunListener。调用它们的started()方法,告诉这些SpringApplicationRunListener,“嘿,SpringBoot应用要开始执行咯!”。
3) 创建并配置当前Spring Boot应用将要使用的Environment(包括配置要使用的PropertySource以及Profile)。
4) 遍历调用所有SpringApplicationRunListener的environmentPrepared()的方法,告诉他们:“当前SpringBoot应用使用的Environment准备好了咯!”。
5) 如果SpringApplication的showBanner属性被设置为true,则打印banner。
6) 根据用户是否明确设置了applicationContextClass类型以及初始化阶段的推断结果,决定该为当前SpringBoot应用创建什么类型的ApplicationContext并创建完成,然后根据条件决定是否添加ShutdownHook,决定是否使用自定义的BeanNameGenerator,决定是否使用自定义的ResourceLoader,当然,最重要的,将之前准备好的Environment设置给创建好的ApplicationContext使用。
7) ApplicationContext创建好之后,SpringApplication会再次借助Spring-FactoriesLoader,查找并加载classpath中所有可用的ApplicationContext-Initializer,然后遍历调用这些ApplicationContextInitializerinitialize(applicationContext)方法来对已经创建好的ApplicationContext进行进一步的处理。
8) 遍历调用所有SpringApplicationRunListener的contextPrepared()方法。
9) 最核心的一步,将之前通过@EnableAutoConfiguration获取的所有配置以及其他形式的IoC容器配置加载到已经准备完毕的ApplicationContext。
10) 遍历调用所有SpringApplicationRunListener的contextLoaded()方法。
11) 调用ApplicationContext的refresh()方法,完成IoC容器可用的最后一道工序。
12) 查找当前ApplicationContext中是否注册有CommandLineRunner,如果有,则遍历执行它们。
13) 正常情况下,遍历执行SpringApplicationRunListener的finished()方法、(如果整个过程出现异常,则依然调用所有SpringApplicationRunListener的finished()方法,只不过这种情况下会将异常信息一并传入处理)

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_44961149/article/details/108513952