SpringBoot八股文

SpringBoot的项目结构是怎么样的?

image-20251120165638699

image-20251120165714585

image-20251120165746866


说一说Spring框架核心特性有哪些? / 好处有哪些?

  1. IOC容器:Spring通过控制反转实现了对象的创建和对象间的依赖关系管理,开发者只需要定义好Bean及其依赖关系,Spring容器就会负责创建和组装这些对象

  2. AOP:面向切面编程,允许开发者横切关注点,例如事务管理、安全控制、日志记录等,可以提高代码的可维护度和可重用性

  3. 事务管理:Spring提供了一致的事务管理接口,支持声明式和编程式事务。开发者可以轻松地进行事务管理,而无需关心具体的事务API

image-20251120165939412

  1. MVC框架:Spring MVC是一种Web框架,采用了模型-视图-控制器(MVC)框架,它支持灵活的URL到页面控制器的映射

  2. 非侵入式设计:它可以使应用程序对框架的依赖最小化


介绍一下IOC

简介:IoC即控制反转的意思,它是一种创建和获取对象的技术思想,依赖注入(DI)是实现这种技术的一种方式。在传统开发过程中,我们需要通过new关键字来创建对象。使用IoC思想开发方式的话,就可以不用new关键字来创建对象,而是通过IoC容器来帮我们完成实例化对象。通过IoC的方式可以大大降低对象之间的耦合度

IOC控制就是对象的创建、初始化、销毁

  • 创建对象:原来是new一个,现在由Spring容器创建;
  • 初始化对象:原来是对象自己通过构造器或者setter方法给依赖的对象赋值,现在是由Spring容器自动注入;
  • 销毁对象:原来是直接给对象赋值null或做一些销毁操作,现在是Spring容器管理生命周期负责销毁对象

IoC的实现机制

  • 反射:Spring IoC容器利用Java的反射机制动态的加载类信息、创建对象实例以及调用对象方法,反射允许在运行时检查类、方法、属性等信息,有利于灵活的实现对象实例化和管理

  • 依赖注入:IOC的核心概念是依赖注入,也就是容器负责应用程序组件之间的依赖关系。Spring通过构造方法注入、属性注入、方法注入,将组件之间的依赖关系描述在配置文件中或使用注解

  • 设计模式-工厂模式:Spring IOC容器通常使用工厂模式来管理对象的创建和生命周期;容器作为工厂负责实例化Bean并管理它们的生命周期,将Bean的实例化过程交给容器来管理

  • 容器实现:Spring IOC容器是实现IOC的核心,通常使用BeanFactory或ApplicationContext来管理Bean。BeanFactory是IOC容器的基本形式,提供基本的IOC功能;ApplicationContext是BeanFactory的扩展,并提供更多企业级功能


什么是Bean?

  • Bean是对象,一个或者多个不限定

  • Bean托管在Spring中一个叫IoC的容器中

  • 我们的程序是由一个个Bean构成的


什么是FactoryBean?

  • FactoryBean是Spring所提供的一种比较灵活的创建Bean的方式,可以通过实现FactoryBean接口中的getObject()方法来返回一个对象,这个对象就是最终的Bean对象;

  • 如果一个对象实现了这个接口,那么它就成为一种特殊的Bean,注册到IoC容器后,如果调用这个对象的 getBean 获得的是 FactoryBean

image-20251120170331542

  • 为什么要有FactoryBean呢? FactoryBean机制被广泛的应用在Spring内部和Spring与第三方框架或组件的整合过程中;假设依赖一个第三方类A,创建这个类的对象比较复杂,同时我们想设置这个类中的某些属性,但是这个类没有提供设置的方法,就可以用一个Bean来封装它

image-20251120170357123

image-20251120170414747


说一说BeanFactory和ApplicationContext

image-20251120170521136

image-20251120170537279

  1. BeanFactory是一个接口,里面定义了getBean()这一个方法,通过实现这个方法可以得到Bean;而ApplicationContext它实现了BeanFactory的实现接口,也属于一个BeanFactory,在ApplicationContext中扩展了很多其他功能,AOP、国际化、资源管理、事件、注解等

  2. BeanFactory的优缺点

    • 优点:应用启动时占用资源很少

    • 缺点:运行速度相对来说慢一点,有可能出现空指针异常的错误

  3. ApplicationContext的优缺点

    • 优点:所有的Bean在启动的时候都进行了加载,系统运行的速度快

    • 缺点:把费时的操作放到系统启动中完成,所有的对象都可以预加载,会导致内存占用大


介绍一下DI


介绍一下AOP

  1. 简介:AOP是面向切面编程,允许开发者横切关注点,例如事务管理、安全控制、日志记录等,可以提高代码的可维护度和可重用性

  2. 在面向切面编程的思想里面,把功能分为两种,一种是核心业务,比如登陆、注册、增、删、改、查都叫核心业务;另一种是周边功能,比如日志、事务管理这些次要的就是周边业务;在面向切面编程中,核心业务功能和周边功能是分别独立进行开发,两者不是耦合的,通过AOP可以将核心业务和周边功能“编织”在一起,这样可以减少系统的重复代码,降低模块之间的耦合度,有利于未来的可拓展性和可维护性

  3. AOP实现机制

    • Spring AOP的实现依赖于动态代理技术。动态代理是在运行时动态生成代理对象,对不是编译时。它允许开发者在运行时指定要代理的接口和行为,从而实现在不修改源码的情况下增强方法的功能。

    • 基于JDK的动态代理:使用java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口实现。这种方式需要代理的类实现一个或多个接口

    • 基于CGLIB的动态代理:当被代理的类没有实现接口时,Spring会使用CGLIB库生成一个被代理的子类作为代理,CGLIB(Code Generation Library)是一个第三方代码生产库,通过继承方式实现代理

image-20251120170740930


动态代理

动态代理是什么?

一种在运行时动态创建代理对象的机制,主要用于在不修改原始类的情况下对方法调用进行拦截和增强

  • 基于接口的代理(JDK动态代理):这种类型的代理要求目标对象必须实现至少一个接口。Java动态代理会创建一个实现了相同接口的代理类,然后在运行时动态生成该类的实例。

  • 基于类的代理(CGLIB动态代理):是一个强大的高性能的代码生成库,它可以在运行时动态生成一个目标类的子类。CGLIB代理不需要目标类实现接口,而是通过继承的方式创建代理类。因此,如果目标对象没有实现任何接口,可以使用CGLIB来创建动态代理


动态代理和静态代理的区别?

代理是一种常用的设计模式,目的是:为其他对象提供一个代理以控制对某个对象的访问,将两个类的关系解耦。代理类和委托类都要实现相同的接口,因为代理真正调用的是委托类的方法

  • 静态代理:由程序员创建或者是由特定工具创建,在代码编译时就确定了被代理的类是一个静态代理。静态代理通常只代理一个类;

  • 动态代理:在代码运行期间,运用反射机制动态创建生成。动态代理代理的是一个接口下的多个实现类。


能用静态代理的方式实现AOP吗?

可行的,但是在实际生产中基本没有人使用,因为有三大硬伤

  • 第一是代码爆炸:如果有100个Service类需要加事务,就需要写100个重复的try-catch提交回滚代码,维护起来很困难

  • 第二是僵化:一旦给业务接口改了个方法名,所有相关的代理类都得跟着改方法名

  • 第三是无法动态筛选:比如你想只给带 @Transactional 注解的方法加事务,静态代理只能写死逻辑,要在实现的方法中判断 Transactional.class,而 Spring AOP 可以在运行时通过切点表达式精准匹配需要增强的方法


反射

  1. 反射指的是在程序运行状态下,对于任意一个类,都可以知道这个类的所有属性和方法,都能调用它的所有属性和方法,Java的反射机制允许在运行时获取类的信息并动态操作对象,即使在编译时不知道具体的类也能实现

  2. 反射有以下几个特性

  • 运行时类信息方法:反射允许程序在运行时获取类的所有信息,属性、方法

  • 动态对象创建:可以使用反射API,Class类的newInstance()方法,动态的创建对象实例,即使在编译时不知道具体的类名

  • 动态方法调用:通过Method类的invoke()方法可以在运行时动态调用对象的方法,包括私有方法

  • 访问和修改字段值:通过Field类的get()和set()方法可以在程序运行时访问和修改对象的字段值,即使是私有的

  1. Spring框架的依赖注入(DI)和控制反转(IoC)
  • 依赖注入(DI):在Spring中,程序员可以通过XML配置文件或者基于注解的方式声明组件之间的依赖关系,当程序运行启动时,Spring容器会扫描这些配置或注解,然后利用反射来实例化Bean(也就是Java对象),并根据配置自动状态它们的依赖

  • 控制反转(IoC):当一个Service类需要依赖另一个DAO类,程序员可以在Service类中使用@Autowired注解,无需自己编写DAO实例的代码,Spring容器在运行解析这个注解,通过反射找到对应的DAO类,实例化它并管理它,将其注入到Service类中。降低了组件之间的耦合度,增强可维护性

  1. 动态代理中反射的应用

为了给所有服务处方法添加日志记录功能,Spring会使用JDK动态代理或CGLIB(如果目标类没有实现接口的话)来创建目标类的代理对象。这个代理对象在调用任何方法的前或后,都会执行记录日志的逻辑,这一切都是在运行时通过反射来动态构建和执行的,无需编码到每个方法调用中。


依赖倒置,依赖注入,控制反转分别是什么?

  1. 控制反转:“控制”指的是对程序的执行流程控制,而“反转”指的是在没有使用框架之前,程序员自己控制整个程序的执行。在使用框架后,整个程序的执行流程交给框架,程序的控制权由程序员交给了框架

  2. 依赖注入:我们不通过new的方式在类内部创建依赖类的对象,而是将依赖类的对象在外部创建好后,通过构造函数、函数参数等方式传递给类使用

以下是几种注入的方式

  • 构造器注入
    image-20251120171505043

  • Setter方法注入
    image-20251120171531213

  • 字段注入
    image-20251120171544650

  1. 依赖倒置:高层模块(高管)不依赖底层模块(员工),它们共同依赖同一个抽象类,可以类似于一种设计模式的设计指南,目的都是为了“解耦”

image-20251120171735051


Spring时如何解决循环依赖的?

什么是循环依赖?

循环依赖指的是两个类中的属性相互依赖对方:例如A类中有B属性,B类中有A属性,从而形成了一个依赖闭环,如下图

  • 相互依赖

image-20251120203655462

  • 三者之间及其以上的依赖

image-20251120203711754

  • 自我依赖

image-20251120203726168


循环依赖问题在Spring中的三种主要情况

  • 第一种:通过构造方法进行依赖注入时产生的循环依赖问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Bean A,构造方法依赖 B
@Component
class A {

private B b;

public A(B b) {
this.b = b;
}
}

// Bean B,构造方法依赖 A
@Component
class B {

private A a;

public B(A a) {
this.a = a;
}
}
  • 第二种:通过setter方法(使用了@Scope注解)进行依赖注入且是在多例(原型)模式下产生的循环依赖问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 原型 Bean C,Setter 依赖 D
@Component
// 把这个 Bean 的作用域改成 prototype(原型模式):
// 每次注入、每次 getBean(),都会创建一个新的实例。
@Scope("prototype")
class C {

private D d;

@Autowired
public void setD(D d) {
this.d = d;
}
}

// 原型 Bean D,Setter 依赖 C
@Component
@Scope("prototype")
class D {

private C c;

@Autowired
public void setC(C c) {
this.c = c;
}
}
  • 第三种:通过setter方法进行依赖注入且是在单例模式下产生的循环依赖问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 原型 Bean E,Setter 依赖 F
@Component
class E {

private F f;

@Autowired
public void setF(F f) {
this.f = f;
}

public void print() {
System.out.println("E 依赖的 F: " + f);
}
}

// 单例 Bean F,Setter 依赖 E
@Component
class F {

private E e;

@Autowired
public void setE(E e) {
this.e = e;
}

public void print() {
System.out.println("F 依赖的 E: " + e);
}
}

只有【第三种方式】的循环依赖问题被Spring解决了,其他两种方式在遇到循环依赖问题时,Spring都会产生异常


三级缓存

Spring在DefaultSingletonBeanRegistry类中维护了三个重要的缓存(Map),称为“三级缓存”

  • singletonObjects(一级缓存):存放的是初始化好的,可以使用的Bean,getBean() 方法返回的也是这里面的Bean,此时Bean已经实例化、属性已经填充、初始化方法已经执行、AOP代理(如果需要的话)也已经生成

交付最终完整的Bean

  • earlySingletonObjects(二级缓存):当一个Bean还在创建的过程中(已经实例化了,但尚未完成属性填充和初始化),但它的引用需要被注入到另一个Bean中,就暂时存放到这里

临时存储已确定的早期引用,避免重复生成代理

  • singletonFactories(三级缓存):存放的是Bean的ObjectFactory工厂对象,这是解决循环依赖的关键,当一个Bean被实例化后(刚掉完构造函数),Spring会创建一个ObjectFactory并将其放入三级缓存。这个工厂的getObject()方法负责返回该Bean的早期引用

负责在对象实例化后立刻暴露对象的生成能力,兼顾AOP代理的提前生成

  • Spring 通过 三级缓存 和 提前暴露未完全初始化的对象引用 的机制来解决【单例作用域 Bean】 的 sette注入方式的循环依赖问题。

Spring解决循环依赖的流程

  • 第一步:创建BeanA的实例并提前暴露工厂

image-20251120210844768

Spring首先调用BeanA的构造函数进行实例化,此时得到一个原始对象(没有填充属性),紧接着,Spring会将一个特殊的ObjectFactory工厂对象存入三级缓存(singletonFactories)。这个工厂的作用是,当其他Bean需要引用BeanA时,它能动态返回当前这个半成品BeanA,此时BeanA的状态是“已实例化但未初始化”

  • 第二步:填充BeanA的属性时触发BeanB的创建

image-20251120210918060

Spring开始为BeanA注入属性,发现它依赖BeanB。于是容器转向创建BeanB,同样先调用其构造函数实例化,并将BeanB对应的ObjectFactory存入三级缓存。至此,三级缓存中同时存在BeanA和BeanB的工厂,都是未完成品

  • 第三步:BeanB属性注入时发现循环依赖
  1. 定位BeanA在哪

image-20251120211011627

  1. BeanA的引用存入二级缓存,清理三级缓存

image-20251120211030396

当Spring试图填充BeanB的属性时,检测到它需要注入BeanA,此时容器开始依赖查找:在一级缓存中未找到BeanA,在二级缓存中也未命中,最终在三级缓存中定位到BeanA的工厂;Spring立即调用BeanA的getObject() 方法,这个方法如果需要AOP代理,会动态生成代理对象;如果不需要,直接返回原始对象。得到的BeanA早期引用会被放入二级缓存(earlySingletonObjects),同时从三级缓存清理BeanA的工厂。

  • 第四步:完成BeanB的生命周期

image-20251120211052089

BeanB获得所有依赖后,Spring执行其初始化方法,将其转化为可以使用的Bean,随后BeanB被提升至一级缓存,二级缓存、三级缓存由于BeanB的临时数据被清除

  • 第五步:回溯完成BeanA的构建

image-20251120211107968

随着BeanB创建完毕,流程回溯到最初中断BeanA属性注入环节。Spring已经具备能将BeanB实例注入BeanA,接着执行BeanA的初始化方法,最终完成初始化的BeanA会进入一级缓存中


Spring为什么用3级缓存解决循环依赖问题?用2级缓存不行吗?


Spring提供了哪些配置方式?

  1. 基于xml配置

bean所需要的依赖项和服务在XML格式的配置文件中定义,这些配置文件通常包含需要bean定义的配置选项

image-20251120211150873

  1. 基于注解配置

可以通过在相关的类,方法或字段声明上使用注解,将bean配置为组件类本身,而不是使用XML来描述bean装配。需要在Spring配置文件中启用它

image-20251120211207534

启动之后,可以基于 Java API 配置,Spring的Java配置是通过使用@Bean和@Configuration来实现

  • @Bean注解扮演与 <bean /> 元素相同的角色

  • @Configuration 类允许通过简单地调用同一个类中的其他 @Bean 方法来定义Bean间依赖关系

image-20251120211402963


将一个类声明为Spring的Bean的注解有哪些?

  1. 我们一般使用 @Autowired 注解自动装配bean,想要把类表示成可用于 @Autowired 注解自动装配的 bean 的类,可以采用以下注解实现

  2. @Component:通用的注解,可以标注任意类为Spring组件,如果一个Bean不知道属于哪个层,可以使用@Component注解标注

  3. @Repository:对应持久层即Dao层,主要用于数据库相关操作

  4. @Service:对应服务层,主要涉及一些复杂的逻辑,需要用到Dao层

  5. @Controller:对应Spring MVC控制层,主要用于接收用户请求并调用Service层返回数据给前端页面


Bean一共有几种作用域?

  1. singleton:默认是单例,一个 IOC 容器内部仅此一个

  2. prototype:原型,多实例

  3. request:每个请求都会新建一个属于自己的 Bean 实例,这种作用域仅存在 Spring Web 应用中

  4. session:一个 http session 中有一个 bean 的实例,这种作用域仅存在 Spring Web 应用中

  5. application:整个 ServletContext 生命周期里,只有一个 bean,这种作用域仅存在 Spring Web 应用中

  6. websocket:一个 WebSocket 生命周期内一个 bean 实例,这种作用域仅存在 Spring Web 应用中


Spring中的单例Bean的线程安全问题?

当多个用户同时发送一个请求,容器会给每一个请求分配一个线程,如果这些线程都涉及到对Bean对象的值进行修改,就需要考虑线程同步问题

无状态Bean和有状态Bean

  1. 无状态就是一次操作,没有可以修改的成员变量,不能保存数据,是线程安全的

  2. 有状态是数据存储功能,可以保存数据,线程不安全

在Spring中无状态的Bean适合用不变模式,使用单例模式策略,这样可以共享实例提高性能;有状态的Bean,在多线程环境下不安全,使用Prototype原型模式策略;单例Bean但每个线程需要独立数据,使用单例+ThreadLocal策略


Spring中的Bean生命周期?

image-20251120211827244

具体一点

image-20251120211837461

1. 先通过构造器创建 Bean 实例;2. 根据属性,注入需要的 Bean;3. 如果实现了 BeanNameAware 等 aware 接口,就执行 aware 注入;4. 把 bean 实例传递 bean 前置处理器的方法 postProcessBeforeInitialization;5. 调用 bean 的初始化的方法;6. 再把 bean 实例传递 bean 后置处理器的方法 postProcessAfterInitialization;7. 这样 bean 就可以使用了;8. 当容器关闭时候,调用 bean 的销毁的方法


什么是Spring的内部Bean?

只有将 Bean 作用另一个 Bean 的属性时,才能将 Bean 声明为内部 Bean。例如,假设有一个 Student 类,其中引用了 Person 类。代码和配置如下

image-20251120212024751

image-20251120212037048


什么是 Spring 装配?

“装配” 是指 Spring 容器通过依赖注入,将多个 Bean(对象)组合绑定,形成完整业务逻辑单元的过程

举个例子:有 UserService依赖于UserDao,Spring容器通过依赖注入,将UserDao注入到UserService的属性 / 构造器中,让两个Bean形成依赖,这个组合绑定的过程就是Bean装配

自动装配的不同模式

  • no:这是默认设置,表示没有自动装配

image-20251120212117492

  • byName:它根据bean的名称注入对象依赖项

image-20251120212132469

  • byType:它根据类型注入对象依赖项

  • 构造函数:它通过调用类的构造函数来注入依赖项

image-20251120212149308

  • autodetect:首先容器会尝试使用构造函数 autowire 装配,如果不行,则尝试通过 byType 自动装配

Spring / SpringBoot框架中用到了哪些设计模式?

  • 工厂设计模式:Spring使用工厂模式通过BeanFactory、ApplicationContext创建Bean对象

  • 代理设计模式:Spring AOP功能的设计

  • 单例设计模式:Spring中的Bean默认都是单例的

  • 模板方法模式:Spring中 jdbcTemplate、hibernateTemplate对数据库操作的类,使用了模板模式

  • 包装器设计模式:项目需要连接多个数据库,不同用户在每次访问中根据需求回去访问不同的数据库,这种模式可以让我们根据用户的需求能够动态切换不同的数据源

  • 观察者模式:Spring事件驱动模型就是观察者模式很经典的一个应用

  • 适配器模式:Spring AOP的增强或通知(Advice)使用到了适配器模式


Spring事务有几个隔离级别?

数据库自定义的隔离级别、读未提交、读已提交、可重复度、序列化


Spring有哪几种事务传播行为?

  • PROPAGATION_REQUIRED(默认) 如果当前存在事务,则用当前事务,如果没有事务则新起一个事务

  • PROPAGATION_SUPPORTS 支持当前事务,如果不存在,则以非事务方式执行

  • PROPAGATION_MANDATORY 支持当前事务,如果不存在,则抛出异常

  • PROPAGATION_REQUIRES_NEW 创建一个新事务,如果存在当前事务,则挂起当前事务

  • PROPAGATION_NOT_SUPPORTED 不支持当前事务,始终以非事务方式执行

  • PROPAGATION_NEVER 不支持当前事务,如果当前存在事务,则抛出异常

  • PROPAGATION_NESTED 如果当前事务存在,则在嵌套事务中执行,内层事务依赖外层事务,如果外层失败,则会回滚内层,内层失败不影响外层。


Spring MVC工作原理是什么?

image-20251120212315460

  1. 客户端发送请求到 DispatcherServlet

  2. DispatcherServlet根据请求信息调用HandlerMapping,解析请求对应的Handler

  3. 解析到对应的Handler(也就是常说的Controller层),开始由HandlerAdapter适配器处理

  4. HandlerAdapter会根据Handler来调用真正的处理器开处理请求,并处理相应的业务逻辑

  5. 处理完请求后,会返回一个ModelAndView对象,Model是返回的数据对象,View是这个逻辑上的View

  6. ViewResolver会根据逻辑View查找实际的View

  7. DispaterServlet会把返回的Model传给View(视图渲染)

  8. 最后,将View返回给客户端


@Controller注解有什么用?

@Controller 注解标记一个类为 Spring Web MVC 控制器 Controller。Spring MVC 会将扫描到该注解的类,然后扫描这个类下面带有 @RequestMapping 注解的方法,根据注解信息,为这个方法生成一个对应的处理器对象。


@RequestMapping 注解有什么用?

@RequestMapping 注解,配置处理器的 HTTP 请求方法,URI等信息,这样才能将请求和方法进行映射。这个注解可以作用于类上面,也可以作用于方法上面,在类上面一般是配置这个控制器的 URI 前缀


@RestController 和 @Controller 有什么区别?

@RestController 注解,在 @Controller 基础上,增加了 @ResponseBody 注解,更加适合目前前后端分离的架构下,提供 Restful API ,返回例如 JSON 数据格式。当然,返回什么样的数据格式,根据客户端的 ACCEPT 请求头来决定。

  • @Controller

image-20251120212448703

  • @RestController

image-20251120212507302


@RequestMapping 和 @GetMapping 注解的不同之处在哪里?

  • @RequestMapping:可注解在类和方法上;@GetMapping 仅可注册在方法上

  • @RequestMapping:可进行 GET、POST、PUT、DELETE 等请求方法;

  • @GetMapping 是 @RequestMapping 的 GET 请求方法的特例,目的是为了提高清晰度。


@RequestParam 和 @PathVariable 两个注解的区别

两个注解都用于方法参数,获取参数值的方式不同,@RequestParam 注解的参数从请求携带的参数中获取,而 @PathVariable 注解从请求的 URI 中获取


返回 JSON 格式使用什么注解?

可以使用 @ResponseBody 注解,或者使用包含 @ResponseBody 注解的 @RestController 注解。

当然,还是需要配合相应的支持 JSON 格式化的 HttpMessageConverter 实现类。例如,Spring MVC 默认使用 MappingJackson2HttpMessageConverter。


Spring Boot

为什么使用springboot?/ 比spring好在哪里?

  1. 简化开发:SpringBoot通过提供一系列的开箱即用的组件和自动配置,简化了项目的配置和开发过程,开发人员可以更专注于业务逻辑实现,而不需要花费过多时间在繁琐的配置上

  2. 快速启动:Spring Boot提供了快速的应用程序启动方式,可通过内嵌的Tomcat、Jetty或Undertow等容器快速启动应用程序,无需额外的部署步骤,方便快捷。

  3. 自动化配置:Spring Boot通过自动配置功能,根据项目中的依赖关系和约定俗成的规则来配置应用程序,减少了配置的复杂性,使开发者更容易实现应用的最佳实践。


怎么理解SpringBoot中的约定大于配置?

约定大于配置是SpringBoot的核心设计理念,它通过预期合理的默认行为和项目规范,大幅减少开发者需要手动配置的步骤,从而提升开发效率和项目标准化程度

理解约定大于配置,可以从以下几个方面来解释

  • 自动化配置:Spring Boot 提供了大量的自动化配置,通过分析项目的依赖和环境,自动配置应用程序的行为。开发者无需配置每个细节,大部分常用的配置都已经预设好了。例如,引入spring-boot-starter-web后,Spring Boot会自动配置内嵌Tomcat和Spring MVC,无需手动编写XML。

  • 默认配置:Spring Boot 为诸多方面提供大量默认配置,如连接数据库、设置 Web 服务器、处理日志等。开发人员无需手动配置这些常见内容,框架已做好决策。例如,默认的日志配置可让应用程序快速输出日志信息,无需开发者额外繁琐配置日志级别、输出格式与位置等。

  • 约定的项目结构:Spring Boot 提倡特定项目结构,通常主应用程序类(含 main 方法)置于根包,控制器类、服务类、数据访问类等分别放在相应子包,如com.example.demo.controller 放控制器类,com.example.demo.service 放服务类等。此约定使团队成员更易理解项目结构与组织,新成员加入项目时能快速定位各功能代码位置,提升协作效率。


Spring Boot中如何实现对不同环境的属性配置文件的支持?

Spring Boot支持不同环境的属性配置文件切换,通过创建application-{profile}.properties文件,其中{profile}是具体的环境标识名称。

例如:application-dev.properties用于开发环境,application-test.properties用于测试环境。。如果要想使用application-dev.properties文件,则在application.properties文件中添加spring.profiles.active=dev。


Spring Boot 的核心注解是哪个?它主要由哪几个注解组成的?

启动类上面的注解是@SpringBootApplication,它也是Spring Boot的核心注解,主要包含了以下3个注解:

  1. @SpringBootConfiguration:继承自@Configuration,二者功能也一致,标注当前类是配置类。可以理解为一个Configuration就是对应的一个Spring的xml版的容器;一个被@Configuration标注的类,相当于一个ApplicationContext.xml文件。

  2. @EnableAutoConfiguration:即把指定的类构造成对象,并放入Spring容器中,使其成为bean对象,作用类似@Bean注解。

  3. @ComponentScan:主要作用是定义包扫描的规则,然后根据定义的规则找出哪些需类需要自动装配到Spring的bean容器中,然后交由Spring进行统一管理。标注了@Controller、@Service、@Repository、@Component 的类都可以别spring扫描到。


你如何理解Spring Boot中的Starter?

Starter可以理解为启动器,它的主要作用如下:

  1. Starter可以维护对应的jar包的版本依赖,使得开发者不需要去关心版本冲突这种容易出错的细节,Starter组件会把对应功能的所有jar包依赖全部导入进来,避免了开发者自己去引入依赖带来的麻烦

  2. Starter内部集成了自动装配的机制,也就是说在程序中依赖对应的starter组件以后,这个组件会自动集成到Spring生态下,并且对于相关Bean的管理,也是基于自动装配机制来完成

  3. 依赖Starter组件后,这个组件对应的功能所需要维护的外部化配置,会自动集成到SpringBoot中,只需要在Application.properties文件里面进行维护就行了,比如Redis这个Starter,只需要在Application.properties文件里面添加Redis的连接信息就可以直接使用了


Spring Boot Starter的工作原理是什么?

在SpringBoot启动的时候,按照约定去读取SpringBoot Starter的配置信息,再根据配置信息对资源进行初始化,并注入到Spring容器中。这样SpringBoot启动完毕后,就已经准备好了一切资源,使用过程中直接注入对应Bean资源即可。