面试题(2)
(一)Java 基础与进阶
1. 请解释 Java 中的多态,说说实现多态的必要条件,以及多态在项目中的实际应用场景。
答案:
多态:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。多态分为编译时多态(方法重载)和运行时多态(方法重写)。
必要条件:
继承
重写
父类引用指向子类对象(向上转型)
应用场景:
在框架中,使用接口或抽象类定义规范,具体实现由子类完成,如Spring中的Bean注入。
在策略模式中,根据不同的上下文选择不同的策略实现。
2. 什么是 Java 内存模型(JMM)?volatile 关键字的作用是什么?它能保证原子性吗?为什么?
答案:
JMM:Java内存模型定义了线程和主内存之间的抽象关系,规定了线程如何以及何时可以看到其他线程修改过的共享变量,以及如何同步地访问共享变量。
volatile作用:
保证可见性:当一个线程修改了volatile变量,新值会立即被刷新到主内存,其他线程读取时会从主内存重新读取。
禁止指令重排序:通过内存屏障保证指令执行顺序。
不能保证原子性:因为volatile只能保证单个读/写操作的原子性,复合操作(如i++)不能保证原子性。
3. HashMap 和 ConcurrentHashMap 的实现原理有什么区别?JDK 1.8 中 HashMap 做了哪些优化?
答案:
HashMap:数组+链表/红黑树(JDK1.8),非线程安全。
ConcurrentHashMap:
JDK1.7:分段锁(Segment)
JDK1.8:CAS + synchronized(锁住链表头节点)
JDK1.8 HashMap优化:
链表长度超过8且数组长度超过64时,链表转为红黑树,提高查询效率。
扩容时,通过高位运算确定扩容后的位置,减少重新计算hash的次数。
4. 线程池的核心参数有哪些?如何设计一个适合业务的线程池?如果线程池任务堆积,会有什么问题?
答案:
核心参数:
corePoolSize:核心线程数
maximumPoolSize:最大线程数
keepAliveTime:空闲线程存活时间
workQueue:工作队列
threadFactory:线程工厂
handler:拒绝策略
设计线程池:根据业务类型(CPU密集型、IO密集型)设置核心参数。CPU密集型任务可设置核心线程数为CPU核数+1,IO密集型任务可设置更多线程(如2*CPU核数)。
任务堆积问题:可能导致内存溢出(如果使用无界队列)或者任务被拒绝(有界队列且拒绝策略为AbortPolicy等)。
5. 什么是 Java 的垃圾回收(GC)?常见的垃圾收集器有哪些?G1 收集器的工作原理是什么?
答案:
GC:自动管理内存,回收不再使用的对象。
常见垃圾收集器:Serial、Parallel、CMS、G1、ZGC等。
G1工作原理:将堆内存划分为多个Region,通过跟踪每个Region的垃圾堆积价值(回收所需时间和空间),在后台维护一个优先级列表,优先回收价值最大的Region。同时使用Remembered Set避免全堆扫描。
(二)Spring Boot & Spring 全家桶
1. 请详细说说 Spring Boot 的自动配置原理,@EnableAutoConfiguration 注解是如何生效的?如何自定义一个 Spring Boot Starter?
答案:
自动配置原理:通过@EnableAutoConfiguration注解导入AutoConfigurationImportSelector,该选择器会读取META-INF/spring.factories中的配置类,根据条件注解(@Conditional)决定是否加载。
自定义Starter:
创建一个配置类,使用@Configuration和@Conditional注解定义自动配置条件。
在META-INF/spring.factories中配置org.springframework.boot.autoconfigure.EnableAutoConfiguration=配置类的全限定名。
2. Spring Bean 的生命周期是什么?Bean 的作用域有哪些?默认的单例 Bean 是线程安全的吗?
答案:
生命周期:实例化→属性赋值→初始化→使用→销毁。
作用域:singleton(默认)、prototype、request、session、global-session。
单例Bean线程安全:不是线程安全的,因为多个线程会共享同一个Bean实例。如果Bean有状态(有成员变量),则需要自己保证线程安全。
3. Spring 中的依赖注入有哪几种方式?构造器注入相比字段注入有什么优势?
答案:
依赖注入方式:构造器注入、setter注入、字段注入。
构造器注入优势:
不可变:通过构造器注入的依赖可以被设置为final,确保依赖不可变。
保证依赖不为空:构造器注入在构建对象时就会注入依赖,避免了空指针异常。
易于测试:测试时可以直接通过构造器注入依赖。
4. Spring Cloud Nacos 的核心功能是什么?它如何实现服务注册与配置中心?和 Eureka 相比有什么区别?
答案:
核心功能:服务注册与发现、配置管理。
服务注册:服务提供者启动时向Nacos服务器注册自己的信息(如IP、端口)。服务消费者从Nacos获取服务列表,通过负载均衡调用服务。
配置中心:将配置信息存储在Nacos服务器,应用程序启动时从Nacos获取配置,并监听配置变化实现动态更新。
与Eureka区别:
Nacos支持服务端主动检查健康状态(如心跳检查),Eureka是客户端心跳。
Nacos支持CP和AP模式切换,Eureka是AP。
Nacos支持配置管理,Eureka不支持。
5. Spring Cloud Gateway 的工作流程是什么?如何实现网关的路由转发、限流、熔断功能?Sentinel 的限流规则有哪些?
答案:
工作流程:客户端请求→Gateway Handler Mapping(确定路由)→Gateway Web Handler(执行过滤器链)→代理服务。
路由转发:通过配置路由规则(如Path、Method等)匹配请求,转发到对应的服务。
限流:使用RedisRateLimiter等限流过滤器,基于令牌桶算法实现。
熔断:使用Hystrix过滤器,当服务出现故障时,快速失败或降级。
Sentinel限流规则:包括流量控制规则(QPS、线程数)、熔断降级规则(慢调用比例、异常比例)、系统保护规则(负载、CPU使用率)等。
(三)MyBatis & 数据库
1. MyBatis 的核心组件有哪些?Mapper 接口为什么不需要实现类就能执行 SQL?动态代理是如何实现的?
答案:
核心组件:SqlSessionFactory、SqlSession、Mapper接口、Executor、StatementHandler、ParameterHandler、ResultSetHandler。
Mapper接口无需实现类:MyBatis使用JDK动态代理为Mapper接口生成代理对象,代理对象会拦截接口方法,根据方法名和参数执行对应的SQL。
动态代理实现:通过MapperProxy类实现InvocationHandler接口,在invoke方法中根据方法名和参数类型找到对应的MappedStatement,然后执行SQL。
2. MyBatis 的一级缓存和二级缓存有什么区别?二级缓存的适用场景是什么?如何避免缓存一致性问题?
答案:
一级缓存:默认开启,基于SqlSession,在同一个SqlSession中有效。
二级缓存:需要手动开启,基于Mapper命名空间,跨SqlSession有效。
适用场景:查询多,更新少,且数据实时性要求不高的场景。
缓存一致性:在更新操作时,清除缓存(设置flushCache=true),避免脏读。
3. MySQL 的事务隔离级别有哪些?InnoDB 默认的隔离级别是什么?它是如何解决幻读的?
答案:
隔离级别:读未提交、读已提交、可重复读、串行化。
默认隔离级别:可重复读。
解决幻读:通过MVCC(多版本并发控制)和间隙锁(Gap Lock)来防止幻读。
4. MySQL 索引的类型有哪些?B + 树索引和哈希索引的区别是什么?如何优化一条慢 SQL(从索引、SQL 语句、表结构角度分析)?
答案:
索引类型:主键索引、唯一索引、普通索引、全文索引、组合索引。
B+树索引 vs 哈希索引:
B+树索引支持范围查询,哈希索引只支持等值查询。
B+树索引适合排序,哈希索引不适合。
优化慢SQL:
索引:检查是否使用索引,避免索引失效(如函数、类型转换、模糊查询前导%)。
SQL语句:避免SELECT *,优化子查询,使用连接查询等。
表结构:考虑分表,选择合适的数据类型等。
5. 什么是 MySQL 的锁?行锁和表锁的适用场景是什么?InnoDB 的行锁是基于什么实现的?如果出现死锁,如何排查和解决?
答案:
锁:保证数据一致性的机制。
行锁:适用于高并发、更新多的场景。
表锁:适用于查询多、更新少的场景,或者小表。
InnoDB行锁实现:基于索引实现,如果查询没有使用索引,则会升级为表锁。
死锁排查:使用
SHOW ENGINE INNODB STATUS
命令查看死锁信息,分析死锁原因。解决死锁通常需要调整业务逻辑,保证锁的获取顺序一致,或者设置锁超时时间。
(四)Redis & 中间件
1. Redis 的数据结构有哪些?说说 zset 的实现原理(跳表),以及 zset 在项目中的应用场景(如排行榜)。
答案:
数据结构:String、List、Hash、Set、ZSet(有序集合)、Bitmaps、HyperLogLog、Stream。
ZSet实现原理:使用跳表(SkipList)和哈希表实现。跳表支持快速范围查询,哈希表支持快速单点查询。
应用场景:排行榜(根据分数排序)、延迟队列(时间戳作为分数)等。
2. Redis 的持久化机制有哪些?RDB 和 AOF 的区别是什么?生产环境中如何选择持久化方案?
答案:
持久化机制:RDB(快照)、AOF(追加日志)。
区别:
RDB:二进制文件,体积小,恢复快,但可能丢失最后一次快照后的数据。
AOF:日志文件,数据安全,但文件大,恢复慢。
生产环境选择:通常同时使用RDB和AOF,用AOF保证数据安全,用RDB进行灾难恢复。
3. 什么是 Redis 的缓存穿透、缓存击穿、缓存雪崩?在你的项目中是如何解决这些问题的?
答案:
缓存穿透:查询不存在的数据,导致每次都要查数据库。
解决:布隆过滤器、缓存空对象。
缓存击穿:热点key过期,大量请求同时访问数据库。
解决:设置热点key永不过期、加互斥锁。
缓存雪崩:大量key同时过期,导致请求打到数据库。
解决:设置不同的过期时间、使用集群提高可用性。
4. 如何用 Redis 实现分布式锁?需要注意哪些问题(如死锁、锁超时、可重入性)?Redisson 的分布式锁有什么优势?
答案:
实现:使用SET命令带NX和EX选项,设置锁和超时时间。
注意问题:
死锁:设置超时时间,避免锁无法释放。
锁超时:业务执行时间超过锁超时时间,可能导致锁被其他线程获取。可以使用看门狗机制自动续期。
可重入性:记录锁的持有者和重入次数。
Redisson优势:提供了可重入锁、锁续期、公平锁等功能,简化了分布式锁的实现。
5. JWT 的组成部分是什么?它的优点和缺点分别是什么?如何解决 JWT 无法主动失效的问题?
答案:
组成部分:Header(头部)、Payload(负载)、Signature(签名)。
优点:无状态、易于扩展、支持跨域。
缺点:无法主动失效、 payload不宜存储敏感信息。
解决无法主动失效:使用黑名单(如Redis存储失效的token)、设置短期的token并配合刷新token机制。
(五)项目与综合能力
1. 在你的权限管理系统中,如何设计用户 - 角色 - 权限的关联关系?如果需要支持 “数据权限”(如不同用户看到不同部门的数据),你会如何实现?
答案:
关联关系:用户-角色(多对多)、角色-权限(多对多)。
数据权限:在权限模型中增加数据权限字段,如部门ID。在查询数据时,通过拦截器或AOP在SQL中自动添加数据权限条件(如部门ID在用户可访问的部门列表中)。
2. 项目中使用 Redis 缓存高频数据时,如何保证缓存与数据库的数据一致性?如果出现缓存和数据库数据不一致,你会如何排查?
答案:
保证一致性:
更新数据时,先更新数据库,再删除缓存(Cache-Aside模式)。
使用消息队列保证最终一致性。
排查不一致:
检查更新操作是否同时更新了缓存和数据库。
检查是否有并发更新导致的数据不一致。
检查缓存是否过期。
3. 如果你负责的项目出现接口响应慢的问题,你会从哪些维度排查(如代码、数据库、中间件、服务器)?说说具体的排查步骤。
答案:
排查维度:
代码:检查是否有慢查询、循环调用、锁竞争等。
数据库:检查慢SQL、索引、锁等。
中间件:检查Redis、MQ等中间件的性能。
服务器:检查CPU、内存、磁盘IO、网络等。
排查步骤:
监控系统定位问题时间段。
查看应用日志和中间件日志。
使用性能分析工具(如Arthas)分析代码性能。
数据库慢查询分析。
服务器资源使用情况分析。
4. 你阅读了 Spring Boot 和 MyBatis 的源码,能举一个源码中的设计模式应用吗?比如 Spring Boot 的自动配置用了什么设计模式,MyBatis 的执行器用了什么设计模式?
答案:
Spring Boot自动配置:使用了条件注解(@Conditional),这类似于策略模式,根据不同的条件选择不同的配置类。
MyBatis执行器:使用了模板方法模式,BaseExecutor定义了执行器的基本流程,具体由子类(如SimpleExecutor、BatchExecutor)实现。
5. 未来 3-5 年的职业规划是什么?你打算如何提升自己的技术深度(如源码、架构设计)和广度(如云原生、微服务)?
答案:
职业规划:成为领域专家(如Java后端、分布式系统),逐步向架构师方向发展。
提升深度:
阅读经典源码(如Spring、MyBatis、Tomcat)。
学习架构设计,参与系统设计和重构。
提升广度:
学习云原生技术(Docker、Kubernetes、Service Mesh)。
学习微服务架构和分布式系统原理。
以上是对这些面试题的简要回答,实际面试中可能需要根据具体情况展开。
面试题详细答案(结合简历技能与企业高频考点)
(一)Java 基础与进阶
1. 请解释 Java 中的多态,说说实现多态的必要条件,以及多态在项目中的实际应用场景
答案:
// 多态示例
interface Animal {void makeSound();
}class Dog implements Animal {@Overridepublic void makeSound() {System.out.println("汪汪");}
}class Cat implements Animal {@Overridepublic void makeSound() {System.out.println("喵喵");}
}// 多态应用
public class Test {public static void main(String[] args) {Animal animal1 = new Dog(); // 向上转型Animal animal2 = new Cat();animal1.makeSound(); // 输出"汪汪" - 运行时确定具体方法animal2.makeSound(); // 输出"喵喵"}
}
必要条件:
继承或实现关系
方法重写
父类引用指向子类对象
项目应用场景:
Spring框架中的依赖注入
策略模式中的不同算法实现
工厂模式返回不同类型的对象
2. 什么是 Java 内存模型(JMM)?volatile 关键字的作用是什么?
JMM核心概念:
主内存 (Main Memory)↑↓ read/load/use/assign/store/write
工作内存 (Working Memory)
volatile作用:
保证可见性:一个线程修改后,其他线程立即可见
禁止指令重排序:通过内存屏障实现
不能保证原子性:如
count++
不是原子操作
示例:
public class VolatileExample {private volatile boolean flag = false;public void writer() {flag = true; // 写操作,立即刷新到主内存}public void reader() {if (flag) { // 读操作,从主内存重新读取// do something}}
}
3. HashMap 和 ConcurrentHashMap 的实现原理区别
HashMap (JDK 1.8):
数组 + 链表/红黑树
非线程安全
扩容时重新哈希
ConcurrentHashMap (JDK 1.8):
数组 + 链表/红黑树 + CAS + synchronized
分段锁思想,只锁单个桶
支持高并发读写
JDK 1.8 HashMap优化:
链表转红黑树(阈值:8)
扩容时优化hash计算
尾插法代替头插法
4. 线程池核心参数与设计
核心参数:
ThreadPoolExecutor(int corePoolSize, // 核心线程数int maximumPoolSize, // 最大线程数 long keepAliveTime, // 空闲线程存活时间TimeUnit unit, // 时间单位BlockingQueue<Runnable> workQueue, // 工作队列ThreadFactory threadFactory, // 线程工厂RejectedExecutionHandler handler // 拒绝策略
)
业务线程池设计:
// CPU密集型任务
ThreadPoolExecutor cpuPool = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(),Runtime.getRuntime().availableProcessors() * 2,60L, TimeUnit.SECONDS,new LinkedBlockingQueue<>(1000)
);// IO密集型任务
ThreadPoolExecutor ioPool = new ThreadPoolExecutor(20, 50, 60L, TimeUnit.SECONDS,new LinkedBlockingQueue<>(2000)
);
任务堆积问题:
内存溢出(无界队列)
任务拒绝(有界队列满)
响应延迟
5. Java垃圾回收与G1收集器
常见GC算法:
标记-清除
标记-整理
复制算法
分代收集
G1收集器特点:
区域化分代式
可预测的停顿时间
整体标记-整理,局部复制
(二)Spring Boot & Spring 全家桶
1. Spring Boot自动配置原理
核心流程:
@SpringBootApplication
→ @EnableAutoConfiguration
→ AutoConfigurationImportSelector
→ spring.factories
→ @Conditional条件判断
→ 创建Bean
自定义Starter步骤:
创建
autoconfigure
模块编写配置类
@Configuration
条件注解
@ConditionalOnClass
等META-INF/spring.factories
中配置
2. Spring Bean生命周期
完整生命周期:
实例化 → 属性赋值 → BeanNameAware → BeanFactoryAware
→ ApplicationContextAware → BeanPostProcessor前置
→ @PostConstruct → InitializingBean → init-method
→ BeanPostProcessor后置 → Bean就绪使用
→ @PreDestroy → DisposableBean → destroy-method
作用域:
singleton(默认)
prototype
request
session
global-session
单例Bean线程安全: 默认不是线程安全的,需要自行保证
3. 依赖注入方式对比
// 字段注入(不推荐)
@Autowired
private UserService userService;// Setter注入
private UserService userService;
@Autowired
public void setUserService(UserService userService) {this.userService = userService;
}// 构造器注入(推荐)
private final UserService userService;
@Autowired
public UserController(UserService userService) {this.userService = userService;
}
构造器注入优势:
不可变性(final字段)
避免空指针
更好的测试性
循环依赖检测
4. Spring Cloud Nacos vs Eureka
Nacos核心功能:
服务注册发现
配置管理
服务健康监测
动态DNS服务
区别对比:
特性 | Nacos | Eureka |
---|---|---|
一致性协议 | CP+AP | AP |
配置中心 | 支持 | 不支持 |
健康检查 | 客户端/服务端 | 客户端心跳 |
雪崩保护 | 支持 | 支持 |
5. Spring Cloud Gateway工作流程
核心流程:
请求 → Gateway Handler Mapping → Gateway Web Handler
→ 过滤器链 → 代理服务 → 响应
限流实现:
spring:cloud:gateway:routes:- id: user_routeuri: lb://user-servicepredicates:- Path=/user/**filters:- name: RequestRateLimiterargs:redis-rate-limiter.replenishRate: 10 # 每秒令牌数redis-rate-limiter.burstCapacity: 20 # 令牌桶容量
Sentinel限流规则:
QPS限流
线程数限流
冷启动
匀速排队
(三)MyBatis & 数据库
1. MyBatis核心组件与动态代理
核心组件:
SqlSessionFactory
SqlSession
Executor
MappedStatement
StatementHandler
Mapper接口动态代理原理:
public class MapperProxy<T> implements InvocationHandler {public Object invoke(Object proxy, Method method, Object[] args) {// 根据接口方法名找到对应的MappedStatement// 执行SQL并返回结果}
}
2. MyBatis缓存机制
一级缓存:
SqlSession级别
默认开启
同一会话中有效
二级缓存:
Mapper级别
需要手动开启
跨SqlSession有效
缓存一致性解决方案:
设置合适的flushInterval
在更新操作中设置flushCache=true
使用@CacheNamespace注解管理
3. MySQL事务隔离级别
隔离级别:
读未提交(脏读、不可重复读、幻读)
读已提交(解决脏读)
可重复读(MySQL默认,解决脏读、不可重复读)
串行化(解决所有问题)
InnoDB解决幻读:
MVCC多版本并发控制
间隙锁(Gap Lock)
Next-Key Lock
4. MySQL索引优化
索引类型:
B+Tree索引(主要)
哈希索引(Memory引擎)
全文索引
空间索引
B+Tree vs 哈希索引:
特性 | B+Tree | 哈希索引 |
---|---|---|
范围查询 | 支持 | 不支持 |
排序 | 支持 | 不支持 |
前缀匹配 | 支持 | 不支持 |
慢SQL优化步骤:
EXPLAIN分析执行计划
检查索引使用情况
优化SQL语句结构
考虑分表分库
5. MySQL锁机制
锁类型:
表锁:MyISAM默认
行锁:InnoDB默认
间隙锁:防止幻读
意向锁:表级锁
死锁排查:
-- 查看死锁信息
SHOW ENGINE INNODB STATUS;-- 查看当前锁信息
SELECT * FROM information_schema.INNODB_LOCKS;
SELECT * FROM information_schema.INNODB_LOCK_WAITS;
死锁解决:
设置锁超时时间
innodb_lock_wait_timeout
保持一致的加锁顺序
使用低隔离级别
(四)Redis & 中间件
1. Redis数据结构与ZSet原理
数据结构:
String、List、Hash、Set、ZSet
Bitmaps、HyperLogLog、Stream
ZSet实现原理:
跳跃表(SkipList) + 哈希表
时间复杂度:O(logN)
支持范围查询和排名
排行榜应用:
ZADD leaderboard 100 "user1"
ZADD leaderboard 200 "user2"
ZREVRANGE leaderboard 0 9 WITHSCORES # 前10名
ZRANK leaderboard "user1" # 获取排名
2. Redis持久化机制
RDB vs AOF对比:
特性 | RDB | AOF |
---|---|---|
数据安全 | 可能丢失数据 | 更高 |
恢复速度 | 快 | 慢 |
文件大小 | 小 | 大 |
性能影响 | 写时复制 | 追加写入 |
生产环境建议:
# 开启混合持久化(Redis 4.0+)
aof-use-rdb-preamble yes# RDB配置
save 900 1
save 300 10
save 60 10000# AOF配置
appendonly yes
appendfsync everysec
3. 缓存问题解决方案
缓存穿透:
布隆过滤器
缓存空对象
public Object getData(String key) {Object value = redis.get(key);if (value == null) {// 布隆过滤器判断是否存在if (!bloomFilter.mightContain(key)) {return null;}// 查询数据库value = db.get(key);if (value == null) {redis.setex(key, 300, ""); // 缓存空值}}return value;
}
缓存击穿:
互斥锁
永不过期+逻辑过期
缓存雪崩:
随机过期时间
集群部署
熔断降级
4. Redis分布式锁实现
基础实现:
-- 加锁
SET lock_key unique_value NX EX 30-- 解锁脚本
if redis.call("get", KEYS[1]) == ARGV[1] thenreturn redis.call("del", KEYS[1])
elsereturn 0
end
Redisson优势:
可重入锁
自动续期
公平锁
红锁算法
5. JWT原理与优化
JWT组成:
Header: 算法和类型
Payload: 业务数据
Signature: 签名
解决无法主动失效:
// 方案1:黑名单机制
public void logout(String token) {long expiration = getExpirationFromToken(token);long ttl = expiration - System.currentTimeMillis();redisTemplate.opsForValue().set("blacklist:" + token, "", ttl, TimeUnit.MILLISECONDS);
}// 方案2:短期token + 刷新token
public class TokenPair {private String accessToken; // 短期,15分钟private String refreshToken; // 长期,7天
}
(五)项目与综合能力
1. 权限系统设计
RBAC模型:
用户 (User) ←多对多→ 角色 (Role) ←多对多→ 权限 (Permission)
数据权限实现:
@Data
public class DataPermission {private String resource; // 资源类型private String operation; // 操作类型private String condition; // 数据条件private String field; // 字段权限
}// SQL拦截器自动添加数据权限条件
@Component
public class DataPermissionInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {// 解析当前用户的数据权限// 在SQL中自动添加WHERE条件return true;}
}
2. 缓存一致性保证
双写策略:
// 先更新数据库,再删除缓存
@Transactional
public void updateUser(User user) {// 1. 更新数据库userMapper.updateById(user);// 2. 删除缓存redisTemplate.delete("user:" + user.getId());// 3. 可选:异步更新缓存messageQueue.send("cache-update", user);
}
不一致排查:
检查更新顺序
检查事务提交
检查网络超时
检查并发冲突
3. 性能问题排查
排查维度:
接口响应慢
应用层
数据库层
中间件层
基础设施层
代码逻辑
JVM GC
线程阻塞
慢SQL
锁等待
连接池
具体步骤:
监控指标定位问题范围
Arthas分析代码性能
慢SQL分析优化
JVM内存和GC分析
网络和系统资源检查
4. 源码设计模式应用
Spring Boot自动配置:
工厂模式:
SpringFactoriesLoader
加载配置模板方法:
AbstractAutoConfiguration
定义流程条件注解:策略模式的具体应用
MyBatis执行器:
// 模板方法模式
public abstract class BaseExecutor implements Executor {public <E> List<E> query(...) {// 模板方法定义执行流程return doQuery(ms, parameter, rowBounds, resultHandler, boundSql);}protected abstract <E> List<E> doQuery(...);
}// 具体实现
public class SimpleExecutor extends BaseExecutor {protected <E> List<E> doQuery(...) {// 具体查询实现}
}