JAVA八股文面试必会-基础篇-2.4 SpringBoot框架

2.4.0 Spring和SpringBoot的区别 ?

Spring是一个开源容器框架, 其核心就是控制反转 (IOC),和面向切面 (AOP), 简单的说就是一个分层的轻量级开源框架

Spring Boot它是一个基于Spring的快速开发框架,可以帮助开发者快速构建基于Spring的应用程序

SpringBoot的核心就是 自动配置依赖版本 管理

2.4.1 讲一讲SpringBoot自动装配的原理

在SpringBoot项目的启动引导类上都有一个注解@SpringBootApplication

这个注解是一个复合注解, 其中有三个注解构成 , 分别是

  • @SpringBootConfiguration : 是@Configuration的派生注解 , 标注当前类是一个SpringBoot的配置类
  • @ComponentScan : 开启组件扫描, 默认扫描的是当前启动引导了所在包以及子包
  • @EnableAutoConfiguration : 开启自动配置(自动配置核心注解)

在@EnableAutoConfiguration注解的内容使用@Import注解导入了一个AutoConfigurationImportSelector.class的类

在AutoConfigurationImportSelector.class中的selectImports方法内通过一系列的方法调用, 最终需要加载类加载路径下META-INF下面的spring.factories配置文件

在META-INF/spring.factories配置文件中, 定义了很多的自动配置类的完全限定路径

这些配置类都会被加载

加载配置类之后, 会配置类或者配置方法上的@ConditionalOnXxxx条件化注解是否满足条件

如果满足条件就会使用@ConfigurationProperties注解从属性配置类中读取相关配置 , 执行配置类中的配置方法 , 完成自动配置

2.4.2 讲一讲SpringBoot启动流程

SpringBoot项目启动都会执行一个main方法 , main方法中调用SpringApplication.run()方法启动程序 , 整个应用启动包括二部分 , 首先先构建SpringApplication对象, 然后执行run方法

构造SpringApplication的时候会进行初始化的工作,初始化的时候会做以下几件事:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    Assert.notNull(primarySources, "PrimarySources must not be null");
    //1. 把primarySources设置到SpringApplication属性中 , 这个primarySources就是启动引导类字节码
    this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
    //2. 判断是否是web程序,并设置到webEnvironment的boolean属性中
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    //3. 创建并初始化ApplicationInitializer,设置到initializers属性中 
    this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
    //4. 创建并初始化ApplicationListener,设置到listeners属性中 
    this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
    //5. 初始化主类mainApplicatioClass
    this.mainApplicationClass = this.deduceMainApplicationClass();
}
  1. SpringApplication构造完成之后调用run方法,启动SpringApplication,run方法执行的时候会做以下几件事:
public ConfigurableApplicationContext run(String... args) {
    //1. 构造一个StopWatch计时器,用来记录SpringBoot的启动时间
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
    this.configureHeadlessProperty();
    //2. 初始化监听器,获取SpringApplicationRunListeners并启动监听,用于监听run方法的执行
    SpringApplicationRunListeners listeners = this.getRunListeners(args);
    listeners.starting();

    Collection exceptionReporters;
    try {
        //3. 创建并初始化ApplicationArguments,获取run方法传递的args参数
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        //4. 创建并初始化ConfigurableEnvironment(环境配置)
        ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
        this.configureIgnoreBeanInfo(environment);
        //5. 打印banner和版本
        Banner printedBanner = this.printBanner(environment);
        //6. 构造Spring容器(ApplicationContext)上下文 , 发布上下文初始化事件(通知监听器执行)
        context = this.createApplicationContext();
        exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
        //7. 准备Spring容器 , 将环境变量 , 监听器 , 应用参数注册到容器中
        this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        //8. 刷新上下文 , 自动装配和启动 tomcat就是在这个方法里面完成的
        this.refreshContext(context);
        this.afterRefresh(context, applicationArguments);
        //9. StopWatch计时器停止计时,日志打印总共启动的时间
        stopWatch.stop();
        if (this.logStartupInfo) {
            (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
        }
    	//10. 发布SpringBoot程序已启动事件
        listeners.started(context);
        //11. 执行自定义的run方法 , 主要就是通知ApplicationRunner 和 CommandLineRunner 接口中的run方法执行 
        this.callRunners(context, applicationArguments);
    } catch (Throwable var10) {
        this.handleRunFailure(context, var10, exceptionReporters, listeners);
        throw new IllegalStateException(var10);
    }

    try {
        //最后发布就绪事件ApplicationReadyEvent,标志着SpringBoot可以处理就收的请求了
        listeners.running(context);
        return context;
    } catch (Throwable var9) {
        this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
        throw new IllegalStateException(var9);
    }
}

2.4.3 你们常用的Starter有哪些

常用的Starter有很多 , 例如 :

  1. spring-boot-starter-web
  2. spring-boot-starter-jdbc
  3. mybatis-spring-boot-starter
  4. spring-boot-starter-test
  5. mybatis-plus-spring-boot-starter
  6. spring-boot-starter-data-redis
  7. spring-boot-starter-data-elasticsearch
  8. spring-boot-starter-data-mongodb
  9. spring-boot-starter-amqp
  10. spring-cloud-starter-openfeign
  11. spring-cloud-starter-alibaba-nacos-discovery

2.4.4 如何定义一个SpringBoot的starter 

我们项目中其实也有自定义Starter , 主要用于封装一些复杂的操作, 让开发更加高效简单 , 我们项目中主要做的Starter主要包括 :

  1. 阿里云OSS的Starter
  2. 阿里云内容安全服务的Starter
  3. Minio的Starter
  4. 聚合支付的Starter
  5. EMQX的Starter

自己定义Starter也不难 , 我们在做的过程中 , 首先会梳理清楚这个Starter的核心功能和作用 , 将一些核心的操作封装为方法 , 封装到一个模板类中, 例如 : AliyunOssTempalte,AliyunGreenTempalte,MinioTemplate之类的 , 这个过程其实比较麻烦

然后就抽取一些经常会变化的配置内容 , 例如 : 一些基本的连接地址配置 , 密钥配置等 , 提供属性配置类(XxxProperties) , 通过@ConfigurationProperties注解读取配置文件到配置类

之后就创建一个自动配置类 , 在自动配置类中配置starter运行需要的一些基本配置 , 例如 : 连接工厂配置, 客户端对象配置 , 一些需要的Bean的配置 以及 需要用到的模板对象配置等 , 设置好生效条件@ConditionalOnXxx

最后就是在项目类加载路径下创建spring.factories配置文件 , 在配置文件中配置自动配置类的全路径就可以了

Starter封装好了之后, 后期项目中使用只需要引入Starter依赖 , 在配置文件中配置所需要的信息 , 就能够完成自动配置 , 注入模板类就能够使用了

2.4.5 SpringBoot支持的配置文件有哪些

springboot支持二种类型的配置文件

  1. properties属性配置文件
  2. yaml配置文件

配置文件必须放置在项目的类加载目录下, 并且名字必须是application

springboot项目在运行的时候会自动加载这些配置文件

还有一种是引导配置文件 , 名字叫bootstrap , 类型可以是properties也可以是yaml , 他会在application配置文件之前加载, 一般用于读取配置中心相关信息

2.4.6 讲一讲SpringBoot项目配置文件的加载顺序

SpringBoot项目的配置形式有很多种 , 如下所示 :

  1. 外部配置中心配置 , 例如 spring cloud configNacos中的配置
  2. 命令行参数配置
  3. 操作系统环境变量配置
  4. 通过spring.config.location参数指定的配置文件
  5. 配置文件(YAML文件、Properties 文件) , Properties配置优先级高于YAML
  6. @Configuration 注解类上的 @PropertySource 指定的配置文件

以上所有形式的配置都会被加载 , 优先级由高到低,当存在相同配置内容时,高优先级的配置会覆盖低优先级的配置;存在不同的配置内容时,高优先级和低优先级的配置内容取并集,共同生效,形成互补配置

2.4.7 运行一个SpringBoot项目有哪些方式

  1. 直接使用 jar -jar 运行
  2. 开发过程中运行main方法
  3. 可以配置插件 , 将springboot项目打war包, 部署到Tomcat中运行

2.4.8 SpringBoot的核心注解是哪个?

Spring Boot的核心注解是@SpringBootApplication , 他由几个注解组成 :

  1. @SpringBootConfiguration: 组合了- @Configuration注解,实现配置文件的功能
  2. @EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项
  3. @ComponentScan:Spring组件扫描 , 默认扫描启动引导类所在包以及子包

2.4.9 Spring Boot 中如何解决跨域问题

SpringMVC项目中使用@CrossOrigin注解来解决跨域问题 , 本质是CORS

@RequestMapping("/hello")
@CrossOrigin(origins = "*")
//@CrossOrigin(value = "http://localhost:8081") //指定具体ip允许跨域
public String hello() {
    return "hello world";
}

SpringBoot项目采用自动配置的方式来配置CORS , 可以通过实现 WebMvcConfigurer接口然后重写addCorsMappings方法解决跨域问题。

@Configuration
public class CorsConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                //是否发送Cookie
                .allowCredentials(true)
                //放行哪些原始域
                .allowedOrigins("*")
                .allowedMethods(new String[]{"GET", "POST", "PUT", "DELETE"})
                .allowedHeaders("*")
                .exposedHeaders("*");
    }
}

在SpringCloud项目中一般都会有网关 , 在网关中可以配置CORS跨域, 这样所有通过网关的请求都解决了跨域问题

spring:
  cloud:
    gateway:
      globalcors:
        cors-configurations:
          '[/**]': # 匹配所有请求
            allowedOrigins: "*" #跨域处理 允许所有的域
            allowedMethods: # 支持的方法
              - GET
              - POST
              - PUT
              - DELETE

2.4.10 SpringBoot项目如何实现配置的热更新 ? 

SpringBoot项目读取配置文件常用的方式有二种 :

  1. 通过@Value注解通过属性名称读取
  2. 通过@ConfigurationProperties属性 , 批量读取配置文件配置到属性配置类

实现热更新的方式也有二种 :

  1. @Value + @RefreshScope注解实现热更新
  2. 通过@ConfigurationProperties注解读取的属性, 自动会热更新
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吉迪恩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值