关于 SpringBoot 你知道多少

关于 SpringBoot 你知道多少


1、简介

​  SpringBoot 从本质上看就是 Spring,它只是帮你做了 那些你 必须去做 又 很繁琐 的 Spring Bean 配置。Spring Boot 本身不提供 Spring 的核心功能,而是作为 Spring 的脚手架框架,使用 “约定优于配置” 的理念,达到 快速构建项目、预置三方配置、开箱即用 的目的。


补充:

​  SpringBoot 两大策略:

  • 开箱即用(Out of box)

    ​  指在开发过程中,通过在 Maven 项目的 pom 文件中添加 相关依赖包,然后使用 对应注解 来替代 繁琐的 xml 配置文件 以管理对象的生命周期。

  • 约定优于配置(Convention over configuration)

    ​  是一种由 SpringBoot 本身来配置 目标结构,由 开发者 在结构中添加信息 的 软件设计范式。


​  SpringBoot 的优点:

  • 可以创建 基于 Maven(或 Gradle)的、独立的 Spring 应用程序,生成 可执行的 Jar 包(或 War包)

  • 内嵌 Tomcat(或 Jetty)等 Servlet 容器

  • 提供 自动配置 的 “starter” 项目对象模型(Project Object Model,简称 POM,定义了 对其它库的 传递依赖)以简化 Maven 配置

  • 尽可能自动配置 Spring 容器

  • 提供 运行时的应用监控

  • 与 云计算 天然集成




2、SpringBoot 的启动流程

​  SpringBoot 项目创建完成会默认生成一个名为 xxxApplication 的入口类,通过该类的 main 方法可以启动 SpringBoot 项目。

​  在 main 方法中,通过 SpringApplication 的静态方法(run 方法)进行 SpringApplication 类的实例化操作,再调用 实例化对象 的 run 方法 来完成 整个项目的初始化和启动。
在这里插入图片描述


扩展:

​  SpringApplication 实例化对象的 run() 源代码

public ConfigurableApplicationContext run(String... args) {
    	// 创建 Stopwatch 对象,记录 run 方法 的启动时长
        StopWatch stopWatch = new StopWatch();
    	// 开始计时
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
    	// 配置 Headless 属性
        this.configureHeadlessProperty();
    	// 获得所有的监听器 (SpringApplicationRunListeners 数组)
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
    	// 启动所有的监听器
        listeners.starting();

        Collection exceptionReporters;
        try {
            // 创建 ApplicationArguments 对象
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            // 环境配置,加载所有的配置属性
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
            // 打印 Banner
            Banner printedBanner = this.printBanner(environment);
            // 创建应用程序上下文容器
            context = this.createApplicationContext();
            // 获取异常报告器
            exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
            // 准备容器,组件对象之间进行关联
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            // 初始化容器
            this.refreshContext(context);
            // 初始化操作之后执行,默认实现为空
            this.afterRefresh(context, applicationArguments);
            // 停止计时
            stopWatch.stop();
            // 打印启动日志
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }

            // 通知监听器:容器启动完毕
            listeners.started(context);
            // 调用 ApplicationRunner 和 CommandLineRunner 的运行方法
            this.callRunners(context, applicationArguments);
        } catch (Throwable var10) {
            // 异常处理
            this.handleRunFailure(context, var10, exceptionReporters, listeners);
            throw new IllegalStateException(var10);
        }

        try {
            // 通知监听器:容器正在运行
            listeners.running(context);
            return context;
        } catch (Throwable var9) {
            // 异常处理
            this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var9);
        }
    }
	return context;
}



3、SpringBoot 的自动装配

3.1、SpringBoot 的自动装配:

​  当我们使用 SpringBoot 时,只需要引入对应的 Starters 依赖,SpringBoot 启动时便会自动加载相关依赖,配置相应的初始化参数,以最快捷、简单的形式对 第三方软件 进行集成。


3.2、SpringBoot 的自动装配流程:

​  SpringBoot 通过 @EnableAutoConfiguration 开启自动配置,加载 spring.factories 中注册的各种 AutoConfiguration 类,当某个 AutoConfiguration 类 满足器注解 @Conditional 指定的生效条件(Starters 提供的依赖、配置 或 Spring 容器中 是否存在某个 Bean 等)时,实例化该 AutoConfiguration 类 中定义的 Bean(组件等),并注入 Spring 容器,就可以完成 依赖框架的自动配置。

在这里插入图片描述



4、SpringBoot 的注解

注解说明
@SpringBootApplicationSpringBoot 的启动注解
@EnableAutoConfigurationSpringBoot 的自动配置注解
@ImportSpringBoot 的加载配置类注解
@ConditionalSpringBoot 的前置条件判断注解

补充:@Conditional 的衍生注解

注解说明
@ConditionalOnProperty在指定的属性有指定值的条件下
@ConditionalOnBean在容器中有指定 Bean 的条件下
@ConditionalOnMissingBean在容器中没有指定 Bean 的条件下
@ConditionalOnSingleCandidate当指定的 Bean 在容器中只有一个(或多个)但指定了primary(首选) Bean 的条件下
@ConditionalOnClass在 classpath(类路径)有指定类的条件下
@ConditionalOnMissingClass在 classpath(类路径)没有指定类的条件下
@ConditionalOnResource在 classpath(类路径)有指定资源的条件下
@ConditionalOnExpression在多个配置(表达式)满足 SpEL 表达式 的条件下
@ConditionalOnJava在运行环境符合指定 Java 版本的条件下
@ConditionalOnJndi在指定资源通过 JNDI 加载的条件下
@ConditionalOnCloudPlatform在运行环境符合指定 云平台 的条件下
@ConditionalOnWebApplication在项目是一个 Web 项目的条件下
@ConditionalOnNotWebApplication在项目不是一个 Web 项目的条件下



5、深入分析 SpringBoot 的自动装配

在这里插入图片描述


5.1、@SpringBootApplication

​  SpringBoot 的自动装配机制由 @SpringBootApplication 集中体现,@SpringBootApplication 主要包含了以下的 3 个注解。

  • SpringBootConfiguration(本质是 @Configuration 配置类)
  • EnableAutoConfiguration(核心注解,负责启动 SpringBoot 的自动装配机制)
  • ComponentScan(启动 @Component 组件的扫描,扫描时 过滤掉 指定的类)

在这里插入图片描述


扩展:

​ @SpringBootApplication = @Configuration + @EnableAutoConfiguration + @ComponentScan



5.2、@SpringBootConfiguration

​  @SpringBootConfiguration 的作用是:标注这个类是一个 SpringBoot 的配置类,@SpringBootConfiguration 主要包括 @Configuration。


5.3、@EnableAutoConfiguration

​  @EnableAutoConfiguration 是 SpringBoot 自动装配机制的核心注解,负责启动 SpringBoot 的自动装配机制,@EnableAutoConfiguration 主要包含以下 2 个注解。

  • @AutoConfigurationPackage(指定默认的包规则)
  • @Import(AutoConfigurationImportSelector.class)(自动配置的后续实现类)

在这里插入图片描述


5.3.1、@AutoConfigurationPackage

​  @AutoConfigurationPackage 的作用是:将 添加该注解的类所在的 package 作为自动配置 package 进行管理,通过 @Import 将 package 注入到 IoC 容器中。

在这里插入图片描述


​ 补充:

  • 实现 ImportBeanDefinitionRegistrar 接口 的类 可以将组件注册到 IoC 容器中

  • ImportBeanDefinitionRegistrar 接口 的实现方法为 registerBeanDefinitions()


AutoConfigurationPackages.Registrar:

​  registerBeanDefinitions() 可以将 指定包中的所有组件 注册到容器中。

在这里插入图片描述

AutoConfigurationPackages.PackageImports:

在这里插入图片描述

​  ClassUtils.getPackageName() 可以获取 标注 @AutoConfigurationPackage 注解的类的全限定名。

在这里插入图片描述


5.3.2、@Import(AutoConfigurationImportSelector.class)
  • @Import 加载 AutoConfigurationImportSelector 类
  • AutoConfigurationImportSelector 类 实现了 DeferredImportSelector 接口,DeferredImportSelector 接口 继承了 ImportSelector 接口
  • selectImports() 会去扫描 META_INF/Spring.factories 配置文件,并将其中的组件完成自动装配

在这里插入图片描述

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    }
    // 1. 获取 @EnableAutoConfiguration 标注类的元信息
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    // 2. 获取自动装配类的候选类名集合
    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    // 3. 移除重复对象
    configurations = removeDuplicates(configurations);
    // 4. 获取自动装配类的排除类名集合
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    // 5. 检查 候选类名集合 是否包括 排除类名集合
    checkExcludedClasses(configurations, exclusions);
    // 6. 移除 候选类名集合 中的 排除类名
    configurations.removeAll(exclusions);
    // 7. 进一步过滤 候选类名
    configurations = getConfigurationClassFilter().filter(configurations);
    // 8. 触发自动装配的导入事件
    fireAutoConfigurationImportEvents(configurations, exclusions);
    // 9. 返回自动装配的元信息
    return new AutoConfigurationEntry(configurations, exclusions);
}

5.3.2.1、获取自动装配类的候选类名集合

在这里插入图片描述

在这里插入图片描述

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
    MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
    if (result != null) {
        return result;
    } else {
        try {
            // 加载配置文件
            Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
            LinkedMultiValueMap result = new LinkedMultiValueMap();

            while(urls.hasMoreElements()) {
                URL url = (URL)urls.nextElement();
                // 获取资源
                UrlResource resource = new UrlResource(url);
                // 加载资源
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                Iterator var6 = properties.entrySet().iterator();

                while(var6.hasNext()) {
                    Entry<?, ?> entry = (Entry)var6.next();
                    String factoryTypeName = ((String)entry.getKey()).trim();
                    String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                    int var10 = var9.length;

                    for(int var11 = 0; var11 < var10; ++var11) {
                        String factoryImplementationName = var9[var11];
                        result.add(factoryTypeName, factoryImplementationName.trim());
                    }
                }
            }

            cache.put(classLoader, result);
            return result;
        } catch (IOException var13) {
            throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
        }
    }
}

while(urls.hasMoreElements()) 会执行三次循环:

  • 第一次循环:

在这里插入图片描述

  • 第二次循环

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • 第三次循环

在这里插入图片描述


5.3.2.2、触发自动装配的导入事件
private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
    // 1. 获取所有的自动装配事件监听器
   List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
   if (!listeners.isEmpty()) {
      AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
      for (AutoConfigurationImportListener listener : listeners) {
         invokeAwareMethods(listener);
          // 2. 执行自动装配事件
         listener.onAutoConfigurationImportEvent(event);
      }
   }
}

5.4、@ComponentScan

​  @ComponentScan 的作用是:扫描 当前类所在的包 及其子包下 包含的注解,将 @Controller / @Service / @Component / @Repository 等注解 加载到 IoC 容器中。


5.5、总结

​  SpringBoot 自动装配 的本质是:通过 Spring 去读取 META-INF/spring.factories 配置文件 并 根据配置文件中的 k-v 键值对(接口 全限定名 - 实现类 全限定名) 批量加载 bean 的过程。


​ 补充:spring.factories
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述


  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值