SpringBoot八股文
SpringBoot八股文
SpringBoot的项目结构是怎么样的?



说一说Spring框架核心特性有哪些? / 好处有哪些?
-
IOC容器:Spring通过控制反转(DI)实现了对象的创建和对象间的依赖关系,开发者只需要定义好Bean及其依赖关系,Spring容器就会负责创建和组装这些对象
-
AOP:面向切面编程,允许开发者横切关注点,例如事务管理、安全控制、日志记录等,可以提高代码的可维护度和可重用性
-
事务管理:Spring提供了一致的事务管理接口。开发者可以轻松地进行事务管理,而无需关心具体的事务API

-
MVC框架:Spring MVC是一种Web框架,采用了模型-视图-控制器(MVC)框架,它支持灵活的URL到页面控制器的映射
-
非侵入式设计:它可以使应用程序对框架的依赖最小化
介绍一下IOC
简介:IOC即控制反转的意思,它是一种创建和获取对象的技术思想,依赖注入(DI)是实现这种技术的一种方式。在传统开发过程中,我们需要通过new关键字来创建对象。使用IOC思想开发方式的话,就可以不用new关键字来创建对象,而是通过IOC容器来帮我们完成实例化对象。通过IOC的方式可以大大降低对象之间的耦合度
IOC控制就是对象的创建、初始化、销毁;
- 创建对象:原来是new一个,现在由Spring容器创建;
- 初始化对象:原来是对象自己通过构造器或者setter方法给依赖的对象赋值,现在是由Spring容器自动注入;
- 销毁对象:原来是直接给对象赋值null或做一些销毁操作,现在是Spring容器管理生命周期负责销毁对象
IOC的实现机制
-
反射:Spring IOC容器利用Java的反射机制动态的加载类信息、创建对象实例以及调用对象方法,反射允许在运行时检查类、方法、属性等信息,有利于灵活的实现对象实例化和管理
-
依赖注入:IOC的核心概念是依赖注入,也就是容器负责应用程序组件之间的依赖关系。
-
设计模式-工厂模式: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

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


说一说BeanFactory和ApplicationContext
-
BeanFactory是一个接口,里面定义了getBean()这一个方法,通过实现这个方法可以用于管理Bean;而ApplicationContext它实现了BeanFactory的实现接口,也属于一个BeanFactory,在ApplicationContext中扩展了很多其他功能,AOP、国际化、资源管理、事件、注解等
-
普通实现BeanFactory接口类,如DefaultListableBeanFactory的优缺点

-
优点:应用启动时占用资源很少
- 缺点:运行速度相对来说慢一点,有可能出现空指针异常的错误
-
-
ApplicationContext的优缺点



-
优点:所有的Bean在启动的时候都进行了加载,系统运行的速度快
- 缺点:把费时的操作放到系统启动中完成,所有的对象都可以预加载,会导致内存占用大
-
介绍一下DI
DI 是实现 IOC 的方式,通过构造器注入、Setter 注入或字段注入,由容器把依赖对象主动注入进去,让对象不再自己创建依赖,从而实现控制反转。让Spring帮我们管理对象之间的依赖关系
几种注入的方式
-
构造器注入(推荐使用)
1
2
3
4
5
6
7
8
9
public class OrderService {
private final UserService userService;
public OrderService(UserService userService) {
this.userService = userService;
}
}
优点:
- 使用final代表注入以后永远不会变
- 不可能出现“忘记注入”的情况,因为如果忘记了,编译阶段会报错,缺少构造函数的参数;
- 不会出现空指针异常
- 方便单元测试
1 | // 如果不加构造函数,就不能修改userService的值 |
1 | // 通过构造注入 |
- 更符合依赖倒置原则(DIP)
- 会在spring启动阶段暴露循环依赖的问题
-
Set方法注入
1
2
3
4
5
6
7
8
9
10
11
12
13
public class OrderService {
private UserService userService;
// 这个注解:告诉 Spring,请用这个方法,把依赖注入到这个 Bean 里。
// Spring会找到 IOC 容器里名字是 UserService 的 Bean
// 将它作为参数传给setUserService()
public void setUserService(UserService userService) {
this.userService = userService;
}
}缺点:
- 对象初始化不完整
- 可能导致空指针异常
-
注解注入
1
2
3
4
5
6
public class OrderService {
private UserService userService;
}缺点:
- 无法测试(不能new以后手动注入)
- 会导致循环依赖难以排查
没有 DI 的话,IOC 为什么无法落地?
因为 IOC 的核心是让对象不再自己创建依赖,而 DI 正是把依赖“注入”进去的机制。如果没有 DI,容器就没办法把依赖交给对象,那对象还是得自己 new,控制权就无法反转,所以 IOC 根本落不了地。
介绍一下AOP
-
简介:AOP是面向切面编程,在不改动代码的情况下,给某段逻辑“加功能”
-
在面向切面编程的思想里面,把功能分为两种,一种是核心业务,比如登陆、注册、增、删、改、查都叫核心业务;另一种是周边功能,比如日志、事务管理这些次要的就是周边业务;在面向切面编程中,核心业务功能和周边功能是分别独立进行开发,两者不是耦合的,通过AOP可以将核心业务和周边功能“编织”在一起,这样可以减少系统的重复代码,降低模块之间的耦合度,有利于未来的可拓展性和可维护性
-
AOP实现机制
- Spring AOP的实现依赖于动态代理技术。动态代理是在运行时动态生成代理对象。它允许开发者在运行时指定要代理的接口和行为,从而实现在不修改源码的情况下增强方法的功能。

动态代理
动态代理是什么?
一种在运行时动态创建代理对象的机制,主要用于在不修改原始类的情况下对方法调用进行拦截和增强
- 基于接口的代理(JDK动态代理):这种类型的代理要求目标对象必须实现至少一个接口
1 | class UserServiceImpl implements UserService {} // OK |
它只能代理这个接口里声明的方法
1 | // 接口类 |
1 | // UserServiceImpl类 |
1 | // 切面类,完成AOP通知类型 |
1 | // JDK Proxy自动生成代理类的结构就是 |
【说明】:当从 Spring 容器中获取到 UserService Bean 并调用 save() 方法时,实际拿到的是一个 JDK 动态代理对象 $Proxy11,而不是UserServiceImpl 本体。
当调用代理对象中的save()方法的时候,代理对象内部再去调 目标对象 UserServiceImpl.save(),在save()方法中又会根据自定义切面类LogAspect中的AOP切面通知处理相应的逻辑
- 基于类的代理(CGLIB动态代理):当目标对象没有实现接口的时候,会创建一个继承这个类的子类,在这个子类中可以代理目标类的所有非 final 方法
动态代理和静态代理的区别?
代理是一种常用的设计模式,目的是:为其他对象提供一个代理以控制对某个对象的访问,将两个类的关系解耦。代理类和委托类都要实现相同的接口,因为代理真正调用的是委托类的方法
-
静态代理:代理类是程序员自己写的,在代码编译时就确定了被代理的类是一个静态代理。静态代理通常只代理一个类;
-
动态代理:一种在运行时动态创建代理对象的机制,主要用于在不修改原始类的情况下对方法调用进行拦截和增强
能用静态代理的方式实现AOP吗?
可行的,但是在实际生产中基本没有人使用,因为有三大硬伤
-
第一是代码爆炸:如果有100个Service类需要加事务,就需要写100个重复的try-catch提交回滚代码,维护起来很困难
-
第二是僵化:一旦给业务接口改了个方法名,所有相关的代理类都得跟着改方法名
-
第三是无法动态筛选:比如你想只给带 @Transactional 注解的方法加事务,静态代理只能写死逻辑,而 Spring AOP 可以在运行时通过切点表达式精准匹配需要增强的方法
【例子】
1 | public interface UserService { |
自己手写的代理类
1 | public class UserServiceProxy implements UserService { |
使用方法
1 | UserService userService = new UserServiceProxy(new UserServiceImpl()); |
反射
-
反射指的是在程序运行状态下,对于任意一个类,都可以知道这个类的所有属性和方法,都能调用它的所有属性和方法,Java的反射机制允许在运行时获取类的信息并动态操作对象,即使在编译时不知道具体的类也能实现
-
反射有以下几个特性
-
运行时类信息方法:反射允许程序在运行时获取类的所有信息,属性、方法
-
动态对象创建:可以使用反射API,Class类的newInstance()方法,动态的创建对象实例,即使在编译时不知道具体的类名
-
动态方法调用:通过Method类的invoke()方法可以在运行时动态调用对象的方法,包括私有方法
-
访问和修改字段值:通过Field类的get()和set()方法可以在程序运行时访问和修改对象的字段值,即使是私有的
-
-
Spring框架的依赖注入(DI)和控制反转(IOC)
-
依赖注入(DI):在Spring中,程序员可以通过XML配置文件或者基于注解的方式声明组件之间的依赖关系,当程序运行启动时,Spring容器会扫描这些配置或注解,然后利用反射来实例化Bean(也就是Java对象),并根据配置自动状态它们的依赖
-
控制反转(IOC):当一个Service类需要依赖另一个DAO类,程序员可以在Service类中使用@Autowired注解,无需自己编写DAO实例的代码,Spring容器在运行解析这个注解,通过反射找到对应的DAO类,实例化它并管理它,将其注入到Service类中。降低了组件之间的耦合度,增强可维护性
-
-
动态代理中反射的应用
Spring 想要在所有方法调用前后统一加上日志,所以它不会让你在每个业务方法里手动写日志逻辑。它会在运行时用 JDK 动态代理或 CGLIB 生成一个“代理对象”。
真正的方法调用不会直接找原对象,而是先进入代理对象。代理对象会通过 反射 调用目标方法,并在调用前后插入日志逻辑。
整个过程都是在运行时动态完成的,不需要你修改任何业务代码。
- Class对象
Class对象不是一个类的实例,而是JVM 里记录该类全部结构信息的数据结构。
一个类被加载(loading过程)的是通过加载字节码 → JVM 创建 Class 对象 → 放入方法区,反射发生在类加载之后、运行阶段(runtime)
通过这个Class对象,我们可以知道里面所有的内容,包括方法,属性
类加载负责把字节码读进 JVM,并创建唯一的 Class 对象;
反射就是利用这个 Class 对象里保存的类结构(方法、字段、构造器)在运行时做动态操作,比如调用方法、访问属性。
1 | JVM 内存 |
依赖倒置,依赖注入,控制反转分别是什么?
-
控制反转(IOC):“控制”指的是对程序的执行流程控制,而“反转”指的是在没有使用框架之前,程序员自己控制对象的生命周期。在使用框架后,对象的创建控制权交给了Spring
-
依赖注入(DI):不通过new的方式在类内部创建依赖类的对象,Spring会将已经创建好的对象给我们
-
依赖倒置:高层模块(高管)不依赖底层模块(员工),它们共同依赖同一个抽象类,可以类似于一种设计模式的设计指南,目的都是为了“解耦”

Spring时如何解决循环依赖的?
什么是循环依赖?
循环依赖指的是两个类中的属性相互依赖对方:例如A类中有B属性,B类中有A属性,从而形成了一个依赖闭环,如下图
- 相互依赖

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

- 自我依赖

循环依赖问题在Spring中的三种主要情况
- 第一种:通过构造方法进行依赖注入时产生的循环依赖问题
1 | // Bean A,构造方法依赖 B |
- 第二种:通过setter方法(使用了@Scope注解)进行依赖注入且是在多例(原型)模式下产生的循环依赖问题
1 | // 原型 Bean C,Setter 依赖 D |
- 第三种:通过setter方法进行依赖注入且是在单例模式下产生的循环依赖问题
1 | // 原型 Bean E,Setter 依赖 F |
只有【第三种方式】的循环依赖问题被Spring解决了,其他两种方式在遇到循环依赖问题时,Spring都会产生异常
三级缓存
Spring在DefaultSingletonBeanRegistry类中维护了三个重要的缓存(Map),称为“三级缓存”,"三级缓存"是按 “Bean 在创建流程中的暴露方式” 来划分的
1 | public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry { |
- singletonObjects(一级缓存):存放的是初始化好的,可以使用的Bean,getBean() 方法返回的也是这里面的Bean,此时Bean已经初始化、实例化、AOP代理(如果需要的话)也已经生成
交付最终完整的Bean
- earlySingletonObjects(二级缓存):当一个Bean还在创建的过程中(已经实例化了,但尚未完成属性填充和初始化),但它的引用需要被注入到另一个Bean中,就暂时存放到这里
提前曝光过的 Bean(半成品的真实对象)
- singletonFactories(三级缓存):存放的是Bean的ObjectFactory工厂对象,这是解决循环依赖的关键,当一个Bean被实例化后(刚调完构造函数),Spring会创建一个ObjectFactory并将其放入三级缓存。这个工厂的getObject()方法负责返回该Bean的早期引用
ObjectFactory,用来创建 Bean(可能被AOP代理)
Spring解决循环依赖的流程
- 第一步:创建BeanA的实例并提前暴露工厂

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

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

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

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

BeanB获得所有依赖后,Spring执行其初始化方法,将其转化为可以使用的Bean,随后BeanB被提升至一级缓存,二级缓存、三级缓存由于BeanB的临时数据被清除
- 第五步:回溯完成BeanA的构建

随着BeanB创建完毕,流程回溯到最初中断BeanA属性注入环节。Spring已经具备能将BeanB实例注入BeanA,接着执行BeanA的初始化方法,最终完成初始化的BeanA会进入一级缓存中
Spring为什么用3级缓存解决循环依赖问题?用2级缓存不行吗?
延迟创建 A 的代理对象(若需要),并把这个代理对象作为“提前暴露的 Bean”提供给别的 Bean 使用。
如果没有三级缓存,Spring 没法动态创建代理,也无法处理“带 AOP 的循环依赖”。
Spring提供了哪些配置方式?
- 基于xml配置
bean所需要的依赖项和服务在XML格式的配置文件中定义,这些配置文件通常包含需要bean定义的配置选项

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

启动之后,可以基于 Java API 配置,Spring的Java配置是通过使用@Bean和@Configuration来实现
-
@Bean注解扮演与
<bean />元素相同的角色 -
@Configuration 类允许通过简单地调用同一个类中的其他 @Bean 方法来定义Bean间依赖关系

@Bean和@Configuration与**@Component和@Autowired**这两组有什么区别?
1 | ┌────────────────────────────────────┐ |
将一个类声明为Spring的Bean的注解有哪些?
-
我们一般使用 @Autowired 注解自动装配bean,想要把类表示成可用于 @Autowired 注解自动装配的 bean 的类,可以采用以下注解实现
-
@Component:通用的注解,可以标注任意类为Spring组件,如果一个Bean不知道属于哪个层,可以使用@Component注解标注 -
@Repository:对应持久层即Dao层,主要用于数据库相关操作 -
@Service:对应服务层,主要涉及一些复杂的逻辑,需要用到Dao层 -
@Controller:对应Spring MVC控制层,主要用于接收用户请求并调用Service层返回数据给前端页面
Bean一共有几种作用域?
-
singleton:默认是单例,一个 IOC 容器内部仅此一个 -
prototype:原型,多实例 -
request:每个请求都会新建一个属于自己的 Bean 实例,这种作用域仅存在 Spring Web 应用中 -
session:一个 http session 中有一个 bean 的实例,这种作用域仅存在 Spring Web 应用中 -
application:整个 ServletContext 生命周期里,只有一个 bean,这种作用域仅存在 Spring Web 应用中 -
websocket:一个 WebSocket 生命周期内一个 bean 实例,这种作用域仅存在 Spring Web 应用中
Spring中的单例Bean的线程安全问题?
当多个用户同时发送一个请求,容器会给每一个请求分配一个线程,如果这些线程都涉及到对Bean对象的值进行修改,就需要考虑线程同步问题
无状态Bean和有状态Bean
-
无状态就是没有可以修改的成员变量,不能保存数据,是线程安全的
-
有状态是数据存储功能,可以保存数据,线程不安全
在Spring中无状态的Bean适合用不变模式,使用单例模式策略,这样可以共享实例提高性能;
有状态的Bean,在多线程环境下不安全,我们可以使用线程安全类(ConcurrentHashMap、CopyOnWriteArrayList),也可以使用ThreadLocal
Spring中的Bean生命周期?

具体一点

1. 先通过构造器创建 Bean 实例;
2. 根据属性,注入需要的 Bean;
3. 如果实现了 一些 aware 接口,就执行 aware 注入;
4. 把 bean 实例传递 bean 前置处理器的方法postProcessBeforeInitialization;
5. 调用 bean 的初始化的方法;
6. 再把 bean 实例传递 bean 后置处理器的方法 postProcessAfterInitialization;(AOP代理在这里生成)
7. 这样 bean 就可以使用了;
8. 当容器关闭时候,调用 bean 的销毁的方法
什么是Spring的内部Bean?
只有将 Bean 作用另一个 Bean 的属性时,才能将 Bean 声明为内部 Bean。例如,假设有一个 Student 类,其中引用了 Person 类。代码和配置如下


什么是 Spring 装配?
“装配” 是指 Spring 容器通过依赖注入,将多个 Bean(对象)组合绑定,形成完整业务逻辑单元的过程
举个例子:有 UserService依赖于UserDao,Spring容器通过依赖注入,将UserDao注入到UserService的属性 / 构造器中,让两个Bean形成依赖,这个组合绑定的过程就是Bean装配
1 | public class UserDao { |
UserService 依赖 UserDao,用 XML 配置自动装配它的不同模式
- no:这是默认设置,表示没有自动装配

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

-
byType:它根据类型注入对象依赖项
-
constructor构造函数:它通过调用类的构造函数来注入依赖项

- autodetect:首先容器会尝试使用构造函数 autowire 装配,如果不行,则尝试通过 byType 自动装配
需要手动扫描才能使用(了解)
1️⃣ 把 XML 放在 resources 目录下(例如:
src/main/resources/spring/beans.xml)文件位置你可以自由决定,比如:
1
2 src/main/resources/beans.xml
src/main/resources/spring/beans.xml这都可以,但重点在下面👇
2️⃣ 再用 @ImportResource 显式告诉 Spring Boot 去加载它
放进去并不会自动生效,你必须在启动类或者配置类中加上:
1
2
3
4 @Configuration
@ImportResource("classpath:beans.xml") // 或 classpath:spring/beans.xml
public class AppConfig {
}这样 Spring Boot 才会去加载 XML 里的 Bean。
Spring / SpringBoot框架中用到了哪些设计模式?
-
工厂设计模式:Spring使用工厂模式通过BeanFactory、ApplicationContext创建Bean对象
-
代理设计模式:Spring AOP功能的设计
-
单例设计模式:Spring中的Bean默认都是单例的
-
模板方法模式:Spring中 jdbcTemplate、hibernateTemplate对数据库操作的类,使用了模板模式
-
包装器设计模式:项目需要连接多个数据库,不同用户在每次访问中根据需求回去访问不同的数据库,这种模式可以让我们根据用户的需求能够动态切换不同的数据源
-
观察者模式:Spring事件驱动模型就是观察者模式很经典的一个应用
-
适配器模式:Spring AOP的增强或通知(Advice)使用到了适配器模式
Spring事务有几个隔离级别?
数据库自定义的隔离级别、读未提交、读已提交、可重复度、序列化
spring事务失效的几种场景
- @Transactional 放在方法上会失效
Spring AOP 默认基于代理(JDK Proxy 或 CGLIB),真正起作用的是代理类,而不是接口本身。
1 | public interface UserService { |
- 调用内部方法导致事务失效
你调用 createOrder() → 调用到了代理对象 → 事务生效
createOrder() 内部调用 this.reduceStock() → 没有经过代理 → 事务失效
所以 reduceStock() 并不是在“事务代理”中执行的。
1 |
|
- 异常没有抛出去
1 | try { |
异常被你 catch + 吞掉 了,导致:
- 代理类 没有接收到异常,事务无法触发 rollback
所以事务不会回滚。
- 访问修饰符导致失效
private 方法上的 @Transactional 不会生效,Spring 事务依赖 AOP 代理,而代理只能拦截 public 方法。
1 |
|
- 多线程下事务不生效
Spring 的事务绑定在线程上,新线程不会继承当前线程的事务上下文。
1 |
|
- 数据库本身不支持事务(如 MyISAM)
Spring 事务只是帮你“开启事务、提交、回滚”,但真正的事务执行者是数据库。
数据库不支持,Spring 也无能为力。
Spring有哪几种事务传播行为?
-
REQUIRED:(默认) 如果当前存在事务,则用当前事务,如果没有事务则新起一个事务 -
supports: 如果当前存在事务,则用当前事务,如果不存在,则以非事务方式执行 -
mandatory:如果当前存在事务,则用当前事务,如果不存在,则抛出异常 -
requires_new: 总是会创建一个新的事物,如果外层有事务,就挂起外层事务 -
not_supported: 无论外层有没有事务,当前方法都不会在事务里运行,如果外层有事务,就挂起外层事务 -
never: 无论外层有没有事务,当前方法都不会在事务里运行,如果外层有事务,就抛出异常 -
nested: 如果当前事务存在,则在嵌套事务中执行,内层事务依赖外层事务,如果外层失败,整个一起回滚(包括内层),内层失败不影响外层。
Spring MVC工作原理是什么?

-
客户端发送请求到 DispatcherServlet,整个MVC的调度中心
-
DispatcherServlet根据请求信息调用HandlerMapping,解析请求对应的Handler
-
解析到对应的Handler(也就是常说的Controller层),开始由HandlerAdapter适配器处理
-
HandlerAdapter会根据Handler来调用真正的处理器开处理请求,并处理相应的业务逻辑
-
处理完请求后,会返回一个ModelAndView对象,Model是返回的数据对象,View是这个逻辑上视图名
-
ViewResolver会根据逻辑View查找实际的View
-
DispaterServlet会把返回的Model传给View(视图渲染)
-
最后,将View返回给客户端
@Component和@Autowired的区别 ?
-
@Component 让 Spring 创建对象并放进容器。
-
@Autowired 让 Spring 把容器中的对象引用给我们。
@Autowired等于Spring到容器里面查找有没有这个类型的Bean,如果有就注入一个,如果没有就报错
@Resource和@Autowired的区别?
- 注入规则不同
@Autowired是按照类型(byType)注入,先在IOC容器中找到匹配的Bean,如果有多个,再根据字段名按名称匹配
@Resource是根据字段名找同名的Bean,如果找不到再根据类型匹配
@Autowired 先按类型找,@Resource 先按名字找。
- 来源不同
@Autowired来自Spring
@Resource来自JDK
- Required属性(只有Autowired有)
@Autowired(required = false)表示注入失败也不会报错。
@Resource 没有这个功能。
@Controller注解有什么用?
@Controller 注解标记一个类为 Spring Web MVC 控制器 Controller。Spring MVC 会将扫描到该注解的类,然后扫描这个类下面带有 @RequestMapping 注解的方法,根据注解信息,为这个方法生成一个对应的处理器对象。
@RequestMapping 注解有什么用?
@RequestMapping 注解,配置处理器的 HTTP 请求方法,URI等信息,这样才能将请求和方法进行映射。这个注解可以作用于类上面,也可以作用于方法上面,在类上面一般是配置这个控制器的 URI 前缀
@RestController 和 @Controller 有什么区别?
@RestController 注解,在 @Controller 基础上,增加了 @ResponseBody 注解,更加适合目前前后端分离的架构下,提供 Restful API ,返回例如 JSON 数据格式。当然,返回什么样的数据格式,根据客户端的 ACCEPT 请求头来决定。
- @Controller

- @RestController

@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
Spring Boot自动装配?
按照各种 @Conditional 条件,自动把你需要的 Bean 装进去,不需要你写 XML 或 @Configuration
1 | @SpringBootApplication |
@EnableAutoConfiguration 会从 spring.factories 中读取所有自带的自动装配类,然后根据 @Conditional 条件决定要不要加载它们。
比如说Tomcat相关的Bean、RedisTemplate相关的Bean
Spring Boot如何使用定时任务?
Spring Boot 定时任务三步走:
1️⃣ 在启动类上开启定时任务:@EnableScheduling
2️⃣ 写一个普通的@Component类
3️⃣ 在方法上加@Scheduled(...)注解,写好执行频率
一、开启定时任务功能
在你的 Spring Boot 启动类上加一个注解:
1 |
|
@EnableScheduling 就是让 Spring Boot 打开“定时任务调度器”。
二、写一个定时任务类
随便建一个类,加上 @Component,让它被 Spring 管理:
1 |
|
✔ 关键点:
- 方法一般是
public void,不需要参数 - 类要能被 Spring 扫描到(有
@Component、@Service都行)
为什么使用springboot?/ 比spring好在哪里?
-
简化开发:SpringBoot通过提供一系列的开箱即用的组件和自动配置,简化了项目的配置和开发过程,开发人员可以更专注于业务逻辑实现,而不需要花费过多时间在繁琐的配置上
-
快速启动:Spring Boot提供了快速的应用程序启动方式,可通过内嵌的Tomcat、Jetty或Undertow等容器快速启动应用程序,无需额外的部署步骤,方便快捷。
-
自动化配置: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个注解:
-
@SpringBootConfiguration:继承自@Configuration,二者功能也一致,标注当前类是配置类。可以理解为一个Configuration就是对应的一个Spring的xml版的容器;一个被@Configuration标注的类,相当于一个ApplicationContext.xml文件。
-
@EnableAutoConfiguration:即把指定的类构造成对象,并放入Spring容器中,使其成为bean对象,作用类似@Bean注解。
-
@ComponentScan:主要作用是定义包扫描的规则,然后根据定义的规则找出哪些需类需要自动装配到Spring的bean容器中,然后交由Spring进行统一管理。标注了@Controller、@Service、@Repository、@Component 的类都可以别spring扫描到。
你如何理解Spring Boot中的Starter?
Starter可以理解为启动器,它的主要作用如下:
-
Starter可以维护对应的jar包的版本依赖,使得开发者不需要去关心版本冲突这种容易出错的细节,Starter组件会把对应功能的所有jar包依赖全部导入进来,避免了开发者自己去引入依赖带来的麻烦
-
Starter内部集成了自动装配的机制,也就是说在程序中依赖对应的starter组件以后,这个组件会自动集成到Spring生态下,并且对于相关Bean的管理,也是基于自动装配机制来完成
-
依赖Starter组件后,这个组件对应的功能所需要维护的外部化配置,会自动集成到SpringBoot中,只需要在Application.properties文件里面进行维护就行了,比如Redis这个Starter,只需要在Application.properties文件里面添加Redis的连接信息就可以直接使用了
Spring Boot Starter的工作原理是什么?
在SpringBoot启动的时候,按照约定去读取SpringBoot Starter的配置信息,再根据配置信息对资源进行初始化,并注入到Spring容器中。这样SpringBoot启动完毕后,就已经准备好了一切资源,使用过程中直接注入对应Bean资源即可。





