一、语雀-Spring面试题
1、Spring中@Service 、@Component、@Repository等注解区别是什么?
✅Spring中@Service 、@Component、@Repository等注解区别是什么?
2、如何在Spring启动过程中做缓存预热
在Spring启动过程中实现缓存预热,可以通过监听ContextRefreshedEvent
事件(如使用ApplicationListener
或@EventListener
注解),在应用上下文初始化完成后触发缓存填充逻辑;通常会在该事件回调中调用服务层方法,主动从数据库或其他数据源加载热点数据存入缓存(如Redis、Caffeine等),确保后续请求可直接命中缓存。示例代码如下:
java
@Component
public class CacheWarmupListener {
@Autowired
private CacheService cacheService;
@EventListener(ContextRefreshedEvent.class)
public void warmUpCache() {
if (!cacheService.isInitialized()) { // 避免重复预热
cacheService.preloadHotData();
cacheService.markInitialized();
}
}
}
需注意:若存在多上下文环境(如父子容器),应通过标志位或分布式锁防止多次执行,并根据业务选择异步执行(@Async
)提升启动速度。
3、@Lazy注解能解决循环依赖吗?
4、Spring 中的 Bean 是线程安全的吗?
5、Spring 中的 Bean 作用域有哪些?
6、Spring的事务在多线程下生效吗?为什么?
7、如何根据配置动态生成Spring的Bean?
1. 基于条件的 Bean 注册
2. 使用@ConfigurationProperties
8、介绍下@Scheduled的实现原理以及用法
1、@Scheduled
的常见用法
@Scheduled
是 Spring 提供的定时任务注解,支持多种调度策略:
- 固定速率执行 :通过
fixedRate = 5000
表示每隔 5 秒执行一次,任务按固定时间间隔触发,无论任务执行耗时多久。 - 固定延迟执行 :通过
fixedDelay = 5000
表示在上一次任务完成后,延迟 5 秒再执行下一次。 - 初始延迟 :通过
initialDelay = 1000
表示首次执行前延迟 1 秒。 - Cron 表达式 :通过
cron = "0 0/1 * * * ?"
支持复杂时间规则(秒、分、时、日、月、周、年)。
示例代码:
java
@Scheduled(fixedRate = 5000)
public void fixedRateTask() { /*...*/ }
@Scheduled(cron = "0 0 12 * * ?")
public void cronTask() { /* 每天中午12点执行 */ }
2、@Scheduled
的实现原理
-
注解扫描与注册 :
Spring 启动时,通过
ScheduledAnnotationBeanPostProcessor
(内置的 BeanPostProcessor)扫描所有带有@Scheduled
注解的方法,将其解析为TaskTrigger
对象(如CronTrigger
或PeriodicTrigger
),并注册到TaskScheduler
中。 -
任务调度器(TaskScheduler) :
默认使用
ThreadPoolTaskScheduler
作为调度器,基于线程池管理任务执行。它会根据触发器的规则(如 Cron 表达式或固定间隔)计算下一次执行时间,并提交任务到线程池。 -
任务执行流程 :
• 若使用
cron
表达式,CronTrigger
会解析表达式生成下一次触发时间。• 若使用
fixedRate
或fixedDelay
,PeriodicTrigger
会根据配置计算触发间隔。• 线程池从队列中取出任务并执行,异常会终止当前任务,但不会影响其他任务。
3、关键配置与注意事项
-
线程池自定义 :
可通过配置
ThreadPoolTaskScheduler
调整线程池参数:java@Bean public ThreadPoolTaskScheduler taskScheduler() { ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); scheduler.setPoolSize(10); // 设置核心线程数 scheduler.setThreadNamePrefix("scheduled-task-"); return scheduler; }
-
方法约束 :
• 被
@Scheduled
修饰的方法必须是public
,返回值应为void
。• 方法参数需为空,否则会抛出异常(如
String[] args
会导致解析错误)。 -
时区与错误处理 :
• 默认使用服务器本地时区,可通过
zone
属性指定时区(如@Scheduled(cron = "...", zone = "GMT+8")
)。• 任务执行异常需手动捕获,避免影响后续任务调度。
4、典型应用场景
- 定时数据同步:如每小时同步数据库到缓存。
- 周期性清理:每日凌晨清理临时文件或过期数据。
- 报表生成:固定时间生成业务报表并发送邮件。
- 心跳检测:通过固定间隔检查服务健康状态。
动态扩展 :若需动态调整任务时间,可结合 ScheduledFuture
取消旧任务并重新注册(需自定义实现)。
9、Spring中创建Bean有几种方式?
1. 通过@Component系列注解
2. 通过@Bean 注解
@Configuration
是 Spring 框架中用于定义配置类的核心注解,标记一个类为 Spring 容器加载的配置源,替代传统的 XML 配置文件。该注解类通过 @Bean
注解的方法直接声明 Bean 实例(方法名即 Bean 名称),并由 Spring 自动调用并注册到应用上下文。其本质是通过 CGLIB 动态代理实现,确保配置类内方法调用时会触发 Spring 的 Bean 管理逻辑(如依赖注入、单例控制等),从而提供更简洁、类型安全的编程式配置方式,是 Java Config 的核心机制之一。
10、Spring 中注入 Bean 有几种方式
1. 使用@Autowired 注解
1.1 字段注入
java
@Component
public class HollisService {
@Autowired
private HollisRepository hollisRepository;
}
1.2 构造器注入
java
@Component
public class HollisService {
private HollisRepository hollisRepository;
@Autowired
public HollisService(HollisRepository hollisRepository){
this.hollisRepository = hollisRepository;
}
}
1.3 setter注入
java
@Component
public class HollisService {
private HollisRepository hollisRepository;
@Autowired
public void setHollisRepository(HollisRepository hollisRepository){
this.hollisRepository = hollisRepository;
}
}
2. 使用 @Resource 和 @Inject 注解
java
@Component
public class HollisService {
@Resource
private HollisRepository hollisRepository;
}
@Component
public class HollisService {
@Inject
private HollisRepository hollisRepository;
}
3. FactoryBean
java
@Configuration
public class HollisService {
@DubboReference(version = "1.0.0")
private HollisRemoteFacadeService hollisRemoteFacadeService;
}
这里用到了@DubboReference把HollisRemoteFacadeService这个 bean给注入进来了。@DubboReference其实是RPC 框架Dubbo 提供的一个注解,他的实现原理其实就是基于 FactoryBean来创建并配置Dubbo服务的代理对象的。
11、同时使用 @Transactional 与 @Async 时,事务会不会生效?
✅同时使用 @Transactional 与 @Async 时,事务会不会生效?
@Transactional
@Transactional
是 Spring 框架中用于声明式事务管理的核心注解。当将其标注在方法或类上时,Spring 会自动为该方法开启一个数据库事务,确保方法内的所有数据库操作(如增删改查)满足原子性------即要么全部成功提交,要么在发生异常时全部回滚。通过设置参数(如 propagation
传播行为、isolation
隔离级别),可以灵活控制事务的边界和并发行为。典型场景如银行转账操作,需保证扣款和入账两个步骤同时成功或失败。
@Async
@Async
是 Spring 提供的异步执行注解,用于将方法标记为非阻塞式调用。当线程调用被 @Async
修饰的方法时,该方法会立即返回,实际执行交由独立的线程池处理(需提前配置 @EnableAsync
和线程池)。此特性适用于耗时操作(如文件上传、远程 API 调用),可避免主线程等待,提升系统吞吐量。但需注意异步方法的返回值需通过 Future
类型处理,且方法内异常不会直接抛出至调用方,需通过回调或日志等方式捕获。
1. 两者在同一方法,事务生效
2. 两者在不同方法,事务只在@Transactional生效,因为线程隔离
12、SpringBoot和传统的双亲委派有什么不一样吗?
Spring Boot与传统双亲委派模型的核心区别
传统双亲委派模型遵循严格的类加载层级(Bootstrap → Extension → Application),确保核心类库的唯一性;而Spring Boot在启动时通过自定义类加载器(如LaunchedURLClassLoader
)打破部分委派规则,优先加载应用自身的类和依赖(如嵌入式容器、自动配置模块),以支持模块化开发和条件化加载(如@ConditionalOnClass
)。这种调整允许应用类覆盖父类加载器的同名类,解决了传统模型在复杂依赖和嵌套JAR场景下的局限性,但需开发者注意潜在的类冲突风险。
传统的采用双亲委派的类加载机制大家都知道,要加载一个类的额时候,先委托父类加载器加载该类;如果父类加载器无法找到(类不存在),才由当前 ClassLoader 加载;这样保证了核心类库(rt.jar)不会被重复加载,避免了类冲突问题。
1. 在JDK 1.8之前,传统的双亲委派如下:
2. 在JDK 1.9中,传统的双亲委派如下:
二、语雀-MySQL面试题
1、什么是索引跳跃扫描
1. 原理
2、MySQL是AP的还是CP的系统?
3、什么是ReadView,什么样的ReadView可见?
4、undolog会一直存在吗?什么时候删除?
原因解释
在基于多版本并发控制(MVCC)的数据库(如MySQL InnoDB)中,Undo Log 用于保存事务修改前的数据版本,以支持其他事务读取历史快照。当一个事务(记为T)提交后,若所有当前活跃的读事务(即尚未提交的读事务)均在T提交之前启动,则这些读事务的快照已不依赖T的Undo Log------因为它们只能看到T提交前的数据版本,而T的修改对它们的视图已不可见。此时,T的Undo Log不再被任何活跃事务需要,可被安全清理,避免占用存储空间。反之,若有活跃读事务在T提交后启动,则这些事务仍需通过T的Undo Log构建其快照,此时清理会导致数据不一致。
5、二级索引在索引覆盖时如何使用MVCC?
6、什么是前缀索引,使用的时候要注意什么?
7、limit的原理是什么?
8、MySQL的update语句什么时候锁行什么时候锁表
9、MySQL表级意向锁作用
MySQL 的意向锁(Intention Locks)确实主要用于在不同粒度的锁(如行级锁和表级锁)之间传递信息,以协调并发操作并避免冲突。它的核心作用是让表级锁 知道当前表中是否存在行级锁,从而避免不同事务之间的锁竞争。
1. 意向锁的类型
- 意向共享锁(IS Lock)
表示事务打算在表中的某些行上加共享锁(S Lock)。 - 意向排他锁(IX Lock)
表示事务打算在表中的某些行上加排他锁(X Lock)。
2. 示例场景
假设有一个表 orders
,包含以下数据:
id | product | quantity |
---|---|---|
1 | A | 10 |
2 | B | 5 |
2.1 事务 A:对单行加排他锁(X Lock)
sql
START TRANSACTION;
-- 对 id=1 的行加排他锁(X Lock)
UPDATE orders SET quantity = 20 WHERE id = 1;
-- 此时,MySQL 自动为该表添加一个 IX 锁(意向排他锁)
- 结果 :事务 A 在
id=1
的行上加了 X 锁,同时在表级加了 IX 锁。 - 含义:告诉其他事务,"我正在修改某些行,但具体是哪些行不确定,你们如果要加表级锁需要先检查"。
2.2 事务 B:尝试对整个表加共享锁(S Lock)
sql
START TRANSACTION;
-- 尝试对整个表加共享锁(S Lock)
LOCK TABLES orders READ;
-- 这里会立即被阻塞,直到事务 A 提交或回滚!
• 原因:表级 S 锁与事务 A 的 IX 锁是兼容的(见下文兼容性规则),因此事务 B 可以加 S 锁。
2.3 事务 C:尝试对整个表加排他锁(X Lock)
sql
START TRANSACTION;
-- 尝试对整个表加排他锁(X Lock)
LOCK TABLES orders WRITE;
-- 这里会被阻塞,直到事务 A 提交或回滚!
• 原因 :表级 X 锁与事务 A 的 IX 锁不兼容。因为 IX 表示"某些行已被锁定",表级 X 锁要求独占整个表,因此必须等待。
2.4 锁兼容性矩阵
锁兼容性 决定了多个事务能否同时持有同一资源的锁,其核心规则是:
• 共享锁 (S)允许其他事务加共享锁,但排斥排他锁(X)。
• 排他锁 (X)完全排斥其他任何锁(包括S和X)。
• 意向锁 (IS/IX)与共享锁兼容(表示行级操作意图),但与排他锁冲突(防止表级独占)。
兼容性矩阵通过定义不同锁的共存规则,平衡并发性能与数据一致性,确保高并发场景下事务不会因锁冲突而阻塞或死锁。
2.5 关键点总结
- 传递信息:意向锁通过表级锁的元数据,告知其他事务"当前表中有行级锁存在"。
- 提高并发性:允许行级锁与表级锁共存(例如 IS/IX 与 S 锁兼容),避免表级锁成为性能瓶颈。
- 层级关系:意向锁是表级锁,但不会直接阻止其他行级锁的操作,仅用于协调表级锁的请求。
通过这种方式,MySQL 在粗粒度(表级)和细粒度(行级)锁之间实现了高效的协作。