1、介绍下 Spring AOP
1.1 统一日志追踪切面(基于注解与切点表达式)
@Aspect
@Component
@Slf4j
public class LoggingAspect {
// 定义切点:标注@OperationLog注解的方法
@Pointcut("@annotation(com.example.annotation.OperationLog)")
public void operationLogPointcut() {}
// 定义切点:Service层所有public方法
@Pointcut("execution(public * com.example.service.*.*(..))")
public void serviceLayerPointcut() {}
@Around("operationLogPointcut() || serviceLayerPointcut()")
public Object logMethodExecution(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
String className = signature.getDeclaringType().getSimpleName();
String methodName = signature.getName();
Object[] args = joinPoint.getArgs();
// 工业级日志规范:MDC追踪请求ID
String traceId = UUID.randomUUID().toString();
MDC.put("traceId", traceId);
try {
log.info("[{}] {}.{}() 入参: {}", traceId, className, methodName,
Arrays.stream(args)
.map(arg -> arg instanceof CharSequence ? "***" : arg) // 敏感信息脱敏
.collect(Collectors.toList()));
long startTime = System.currentTimeMillis();
Object result = joinPoint.proceed();
long costTime = System.currentTimeMillis() - startTime;
log.info("[{}] {}.{}() 耗时: {}ms 结果: {}", traceId, className,
methodName, costTime, truncateResult(result));
return result;
} catch (BusinessException e) {
log.error("[{}] 业务异常: {} | 错误码: {}", traceId, e.getMessage(), e.getCode());
throw e;
} catch (Exception e) {
log.error("[{}] 系统异常: {}", traceId, e.getMessage(), e);
throw new SystemException("SYSTEM_ERROR");
} finally {
MDC.clear();
}
}
private String truncateResult(Object result) {
// 防止日志过大,截断复杂对象
return result != null ? result.toString().substring(0, Math.min(100, result.toString().length())) : "null";
}
}
工业级特性:
- 支持多切点组合(注解+包路径)
- MDC实现全链路追踪
- 敏感参数脱敏
- 异常分类处理(业务异常/系统异常)
- 日志截断防止OOM
1.2 分布式锁切面(基于 Redisson)
@Aspect
@Component
@RequiredArgsConstructor
public class DistributedLockAspect {
private final RedissonClient redissonClient;
@Around("@annotation(redisLock)")
public Object around(ProceedingJoinPoint joinPoint, RedisLock redisLock) throws Throwable {
String lockKey = generateLockKey(joinPoint, redisLock);
RLock lock = redissonClient.getLock(lockKey);
try {
boolean acquired = lock.tryLock(redisLock.waitTime(), redisLock.leaseTime(), TimeUnit.SECONDS);
if (!acquired) {
throw new ConcurrentAccessException("资源繁忙,请重试");
}
return joinPoint.proceed();
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
private String generateLockKey(ProceedingJoinPoint joinPoint, RedisLock redisLock) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
return "lock:" + redisLock.prefix() + ":" +
Arrays.stream(joinPoint.getArgs())
.filter(arg -> arg instanceof LockKey)
.findFirst()
.map(arg -> ((LockKey) arg).getId())
.orElseThrow(() -> new IllegalArgumentException("未找到LockKey参数"));
}
}
// 自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RedisLock {
String prefix() default "";
int waitTime() default 3;
int leaseTime() default 10;
}
工业级特性:
- 结合业务参数生成动态锁Key
- 超时自动释放锁防止死锁
- 线程级锁持有检查
- 自定义锁参数配置
- 异常降级处理
1.3 权限校验切面(基于 RBAC 模型)
@Aspect
@Component
@RequiredArgsConstructor
public class AuthorizationAspect {
private final PermissionService permissionService;
@Before("@annotation(requiresPermission)")
public void checkPermission(JoinPoint joinPoint, RequiresPermission requiresPermission) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String resource = requiresPermission.resource();
String action = requiresPermission.action();
if (!permissionService.checkPermission(auth.getName(), resource, action)) {
throw new AccessDeniedException("无操作权限: " + resource + ":" + action);
}
}
@AfterReturning(pointcut = "@annotation(com.example.annotation.AuditLog)",
returning = "result")
public void auditLog(JoinPoint joinPoint, Object result) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
AuditLog auditLog = signature.getMethod().getAnnotation(AuditLog.class);
auditService.log(
auditLog.module(),
auditLog.type().name(),
JSON.toJSONString(result),
SecurityUtils.getCurrentUser()
);
}
}
// 权限注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequiresPermission {
String resource(); // 资源标识
String action(); // 操作类型
}
// 审计日志注解
public @interface AuditLog {
String module();
AuditType type() default AuditType.QUERY;
}
工业级特性:
- RBAC权限模型集成
- Spring Security上下文集成
- 审计日志异步记录
- 操作结果持久化
- 注解驱动配置
Spring AOP 核心原理
Spring AOP 的核心价值在于将横切关注点从业务代码中剥离,实现高内聚、低耦合的架构设计。其通过动态代理机制,在不修改原有代码的前提下,为系统提供可插拔的增强功能,是构建可维护、可扩展的企业级应用的关键技术。
动态代理机制:
-
JDK动态代理:基于接口,
Proxy
+InvocationHandler
实现 -
CGLIB代理:基于继承,
MethodInterceptor
实现 - Spring优先使用JDK代理,无接口时自动切换CGLIB
织入时机:
- 编译时织入(AspectJ):通过编译器修改字节码
- 运行时织入(Spring AOP):通过动态代理实现
性能优化:
- 使用
@Around
替代多个Advice类型组合 - 避免在切点表达式中使用
execution(* com..*.*(..))
等宽泛匹配 - 异步处理耗时操作(如审计日志)
面试可扩展方向:
- 如何解决内部方法调用AOP失效问题(通过AopContext)
- 多个切面的执行顺序控制(@Order注解)
- AOP与事务管理的协同工作原理(TransactionInterceptor)
2、Spring AOP 内部调用失效问题
问题原因
Spring AOP 基于动态代理实现,内部方法调用(如 this.method()
)会绕过代理对象,直接调用目标对象的方法,导致切面逻辑失效。核心原理如下:
// 代理对象伪代码
public class ServiceProxy extends ServiceImpl {
private ServiceImpl target;
public void methodB() {
// 1. 执行切面逻辑
// 2. 调用 target.methodB()
}
}
// 当 target.methodB() 内部调用 this.methodA() 时,直接走原始对象方法,而非代理逻辑
解决方案与代码示例
通过 AopContext
获取当前代理对象
配置:启用代理暴露
@SpringBootApplication
@EnableAspectJAutoProxy(exposeProxy = true) // 关键配置
public class Application { ... }
代码改造:
@Service
public class PaymentService {
public void transfer(String userId) {
// 内部调用时强制走代理对象
((PaymentService) AopContext.currentProxy()).deductFee(userId);
}
@RateLimiter // 自定义限流注解
public void deductFee(String userId) { ... }
}
注意:需捕获 IllegalStateException
(非代理环境调用时抛出)
注入自身代理(Self-Injection)
代码实现:
@Service
public class OrderService {
@Autowired // 注入Spring管理的代理对象
private OrderService selfProxy;
public void createOrder() {
selfProxy.validateStock(); // 通过代理对象调用
}
@InventoryLock // 库存锁切面
public void validateStock() { ... }
}
要点:
- Spring 通过三级缓存解决循环依赖问题
- 避免在构造函数中使用
selfProxy
(此时代理未完成初始化)
拆分服务层(设计模式优化)
代码重构:
// 将需增强的方法拆分到独立Service
@Service
public class ValidationService {
@PermissionCheck // 权限校验切面
public void validateUserRole(String userId) { ... }
}
// 原服务类通过依赖注入调用
@Service
@RequiredArgsConstructor
public class UserService {
private final ValidationService validationService;
public void updateProfile(String userId) {
validationService.validateUserRole(userId); // 跨Bean调用触发AOP
}
}
优势:符合单一职责原则,彻底规避自调用问题
对比与选型建议
方案 | 适用场景 | 优缺点 |
---|---|---|
AopContext |
快速修复遗留代码 | 代码侵入性强,需全局配置暴露代理 |
自身代理注入 | 中度耦合的业务逻辑 | 需注意循环依赖,适合分层清晰的架构 |
服务层拆分 | 新项目或重构场景 | 设计更优雅,但会增加类数量 |
3、Spring AOP 与事务管理的协同工作原理
核心机制
Spring 事务管理通过 AOP 动态代理实现声明式事务,将事务控制逻辑(开启、提交、回滚)与业务代码解耦。其协同工作流程如下:
3.1 代理对象生成
实现原理:
-
动态代理:Spring 通过
BeanPostProcessor
在 Bean 初始化后阶段创建代理对象(JDK 动态代理或 CGLIB 代理)。 -
注解驱动:
@Transactional
注解标记的方法会被代理拦截。
代码示例:
// 原生业务类(无事务逻辑)
@Service
public class OrderService {
@Transactional
public void createOrder(Order order) {
// 业务操作(如插入订单、扣减库存)
}
}
// Spring 生成的代理类伪代码
public class OrderServiceProxy extends OrderService {
private PlatformTransactionManager transactionManager;
@Override
public void createOrder(Order order) {
TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
try {
super.createOrder(order); // 执行目标方法
transactionManager.commit(status); // 提交事务
} catch (Exception e) {
transactionManager.rollback(status); // 回滚事务
throw e;
}
}
}
3.2 事务切面拦截
AOP 切面核心逻辑:
-
切点定义:拦截所有
@Transactional
注解的方法。 - 环绕通知:在方法执行前后管理事务生命周期。
代码实现(简化版 TransactionInterceptor
逻辑):
@Aspect
@Component
public class TransactionAspect {
@Autowired
private PlatformTransactionManager transactionManager;
@Around("@annotation(org.springframework.transaction.annotation.Transactional)")
public Object manageTransaction(ProceedingJoinPoint joinPoint) throws Throwable {
TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
try {
Object result = joinPoint.proceed(); // 执行目标方法
transactionManager.commit(status); // 提交事务
return result;
} catch (RuntimeException e) {
transactionManager.rollback(status); // 回滚事务
throw e;
}
}
}
3.3 事务属性配置
通过 @Transactional
注解参数定义事务行为:
@Transactional(
propagation = Propagation.REQUIRED, // 事务传播行为(默认加入当前事务)
isolation = Isolation.DEFAULT, // 事务隔离级别(默认与数据库一致)
rollbackFor = Exception.class, // 触发回滚的异常类型
timeout = 5 // 事务超时时间(秒)
)
public void updateInventory(Item item) {
// 库存更新逻辑
}
3.4 事务管理器协同
关键组件:
-
PlatformTransactionManager
:事务操作的核心接口(如DataSourceTransactionManager
)。 -
事务同步:通过
TransactionSynchronizationManager
绑定当前线程的事务上下文。
事务传播行为示例(嵌套事务):
@Service
public class PaymentService {
@Autowired
private PaymentService selfProxy; // 注入代理对象解决内部调用问题
@Transactional
public void processPayment(Order order) {
// 主事务逻辑
selfProxy.auditPayment(order); // 嵌套事务(REQUIRES_NEW)
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void auditPayment(Order order) {
// 审计逻辑(独立事务)
}
}
4、Spring 事务传播机制详解
核心概念
Spring 事务传播机制定义多个事务方法嵌套调用时的事务边界规则,通过 @Transactional(propagation = Propagation.XXX)
配置。以下用代码示例详解 7 种传播行为:
1. REQUIRED(默认)
规则:存在事务则加入,否则新建事务。
场景:通用业务逻辑(如订单创建与库存扣减)。
代码:
@Service
public class OrderService {
@Transactional
public void createOrder() {
orderDao.save(); // 操作1
inventoryService.deduct(); // 操作2(同一事务)
}
}
效果:若 deduct()
抛出异常,save()
和 deduct()
均回滚。
2. REQUIRES_NEW
规则:无论是否存在事务,都新建事务并挂起当前事务。
场景:需独立提交的操作(如日志记录)。
代码:
@Service
public class LogService {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveLog() {
logDao.insert(); // 独立事务提交,不受外层事务影响
}
}
效果:即使外层事务回滚,日志仍保留。
3. NESTED
规则:嵌套事务,依赖外层事务提交,但可部分回滚。
场景:主流程与子流程解耦(如订单提交与优惠券核销)。
代码:
@Service
public class CouponService {
@Transactional(propagation = Propagation.NESTED)
public void useCoupon() {
couponDao.update(); // 嵌套事务
}
}
效果:若 useCoupon()
失败,仅回滚优惠券操作,不影响订单主事务。
4. SUPPORTS
规则:存在事务则加入,否则非事务执行。
场景:读操作或非关键逻辑。
代码:
@Transactional(propagation = Propagation.SUPPORTS)
public List<Order> queryOrders() {
return orderDao.findAll(); // 无事务时直接查询
}
5. MANDATORY
规则:强制要求存在事务,否则抛异常。
场景:核心业务方法(如支付)。
代码:
@Transactional(propagation = Propagation.MANDATORY)
public void processPayment() {
paymentDao.update(); // 必须在事务中执行
}
6. NOT_SUPPORTED
规则:非事务执行,挂起当前事务。
场景:缓存更新或统计报表。
代码:
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void updateCache() {
redisTemplate.set("key", "value"); // 非事务执行
}
7. NEVER
规则:禁止事务,存在事务则抛异常。
场景:调用第三方接口。
代码:
@Transactional(propagation = Propagation.NEVER)
public void callThirdAPI() {
httpClient.post(...); // 必须非事务
}
面试要点总结
传播行为 | 核心特点 | 典型场景 |
---|---|---|
REQUIRED | 默认,事务合并 | 订单创建、库存扣减 |
REQUIRES_NEW | 独立事务,互不影响 | 日志记录、异步消息 |
NESTED | 嵌套事务,部分回滚 | 主流程+子流程解耦 |
SUPPORTS | 灵活适配事务环境 | 读操作、非关键逻辑 |
MANDATORY | 强制事务环境 | 支付、核心业务校验 |
NOT_SUPPORTED | 非事务执行 | 缓存更新、统计报表 |
NEVER | 禁止事务 | 第三方接口调用 |
源码入口:TransactionInterceptor
类实现事务拦截逻辑。
通过以上代码示例和场景解析,可清晰掌握 Spring 事务传播机制的核心原理与工业级实践。
5、什么是 Spring IoC 和 DI
5.1 IoC(控制反转)
核心概念
IoC 是一种设计思想,通过将对象的创建和管理控制权从程序代码转移到外部容器,实现对象间依赖关系的解耦。传统开发中,程序员需要在代码中显式调用 new
创建对象(称为“正转”),而 IoC 容器接管了这一过程,由容器负责对象的创建、生命周期管理和依赖注入(称为“反转”)。
关键点
控制权的反转
传统方式由程序主动创建依赖对象(如new B()
),而 IoC 容器负责对象的创建和资源管理。这种反转降低了代码的耦合度,使得模块间依赖关系更灵活。-
解决的问题
- 耦合度高:传统代码中,对象直接依赖具体实现类,导致修改依赖时需改动多处代码。
- 可测试性差:依赖硬编码使得单元测试难以模拟依赖对象。
实现方式
IoC 容器(如 Spring 的ApplicationContext
)通过配置文件或注解管理对象的创建和依赖关系。例如,Spring 容器会根据 XML 或@Component
注解自动实例化对象并注入依赖。
5.2 DI(依赖注入)
核心概念
DI 是 IoC 的具体实现技术,通过容器在运行时动态将依赖对象注入到目标组件中。其核心目标是解耦,使组件不直接依赖具体实现,而是依赖抽象接口。
实现方式
构造函数注入
通过类的构造函数传递依赖对象。例如:
public class Car {
private Engine engine;
public Car(Engine engine) { // 容器自动注入
this.engine = engine;
}
}
这种方式强制依赖在对象创建时完成,确保依赖完整性。
Setter 方法注入
通过 Setter 方法动态设置依赖。例如:
public class UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) { // 容器调用此方法注入
this.userDao = userDao;
}
}
适用于可选依赖或需要动态变更的场景。
接口注入(较少使用)
依赖对象通过实现特定接口注入,Spring 框架中不常用此方式。
DI 的优势
- 解耦:对象无需关心依赖的具体实现,只需声明所需接口。
- 可测试性:通过注入 Mock 对象,单元测试更易实现。
- 可扩展性:更换依赖实现时只需修改容器配置,无需改动业务代码。
5.3 IoC 与 DI 的关系
从属关系
- DI 是 IoC 的一种实现方式。IoC 强调控制权的转移,而 DI 描述如何通过注入依赖实现这一目标。
实际应用
- Spring 框架通过
@Autowired
注解或 XML 配置实现依赖注入,容器负责管理对象的生命周期。 - 例如,在分层架构中,Controller 依赖 Service,Service 依赖 DAO,所有对象的创建和依赖均由容器管理。
总结
- IoC 是设计思想,核心是控制权的反转,由容器管理对象。
- DI 是具体实现,通过动态注入依赖解耦组件。
- 两者共同构建了灵活、可扩展的架构,是 Spring 等框架的核心基础。
6、Spring IoC 容器初始化流程
核心步骤(示意图简化版):
参考:Spring IOC容器初始化
6.1 Bean 定义的定位
- 目标:找到所有 Bean 的定义信息。
-
实现方式:
-
配置形式:XML(如
<beans>
)、注解(如@Component
)、Java Config 等。 -
资源定位:通过
Resource
(如ClassPathResource
)读取配置文件或扫描注解。 -
注册到容器:将解析后的
BeanDefinition
存储到 Bean 定义注册表(BeanDefinitionRegistry
)。
-
配置形式:XML(如
-
关键对象:
-
BeanDefinition
:描述 Bean 的类名、作用域、依赖关系等元数据。 -
BeanDefinitionRegistry
:存储所有 Bean 定义的注册中心。
-
6.2 Bean 的创建(延迟初始化)
-
触发条件:第一次调用
getBean()
请求某个 Bean 时触发创建。 -
创建过程:
- 根据
BeanDefinition
中的类信息(如class="com.example.UserService"
)通过反射实例化对象。 - 填充依赖属性(如通过 Setter 或构造函数注入其他 Bean)。
- 调用初始化方法(如
@PostConstruct
标记的方法)。
- 根据
-
示例代码逻辑:
// 伪代码:容器内部逻辑 BeanDefinition definition = registry.getBeanDefinition("userService"); Class<?> clazz = Class.forName(definition.getClassName()); Object bean = clazz.newInstance(); // 反射实例化 populateDependencies(bean); // 注入依赖 initializeBean(bean); // 执行初始化方法
6.3 Bean 的缓存(单例模式)
-
存储位置:单例 Bean 缓存(
Singleton Bean Cache
)。 -
缓存策略:
- 单例 Bean:默认作用域,容器生命周期内只创建一次,后续直接从缓存获取。
-
原型 Bean(Prototype):每次
getBean()
创建新对象,不缓存。
-
缓存结构:
// 伪代码:单例缓存 Map<String, Object> singletonCache = new ConcurrentHashMap<>(); singletonCache.put("userService", userServiceInstance);
6.4 Bean 的使用
-
获取方式:通过
getBean()
或自动注入(如@Autowired
)。 -
执行逻辑:
- 若 Bean 已缓存,直接返回单例对象。
- 若未缓存,触发步骤 2 的创建流程。
-
代码示例:
// 应用程序中获取 Bean UserService userService = applicationContext.getBean("userService"); userService.execute(); // 使用 Bean
流程总结
步骤 | 输入 | 输出 | 关键组件 |
---|---|---|---|
1. 定位定义 | XML/注解配置 |
BeanDefinition 注册表 |
Resource 、BeanDefinitionReader
|
2. 创建 Bean | BeanDefinition |
实例化对象 | 反射、依赖注入处理器 |
3. 缓存 Bean | 实例化对象 | 单例缓存 | SingletonBeanRegistry |
4. 使用 Bean | 缓存或新建对象 | 应用程序调用 | ApplicationContext |
以下是基于 Spring IoC 容器初始化流程的详细解析,结合源码实现说明从配置文件到 Bean 工厂注册的核心过程:
7、 Bean解析注册过程
核心入口:AbstractApplicationContext.obtainFreshBeanFactory()
作用:完成配置文件的读取、解析,并将 BeanDefinition
注册到 BeanFactory
中,最终构建可用的 IoC 容器。
7.1 读取 XML 配置文件并定位资源
-
资源定位
Spring 通过Resource
接口统一抽象资源(如 XML 文件),支持多种资源类型(类路径、文件系统、URL 等)。
源码片段(以ClassPathXmlApplicationContext
为例):// 指定配置文件路径(如 config/spring/local/appcontext-client.xml) String[] configLocations = {"config/spring/local/appcontext-client.xml"}; this.setConfigLocations(configLocations); // 解析并存储配置路径
-
setConfigLocations()
方法会将路径解析为标准化格式,处理占位符(如${env}
)和环境变量。 - 最终生成
Resource
对象,用于后续加载和解析。
-
7.2 创建 BeanFactory 实例
-
默认实现
Spring 使用DefaultListableBeanFactory
作为默认的BeanFactory
实现,支持 Bean 的注册、依赖注入等功能。
源码片段:// 创建 BeanFactory 实例 DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
- 该工厂类维护了
BeanDefinitionMap
存储所有 Bean 的定义信息。 - 通过
setSerializationId()
方法设置序列化 ID,支持容器的序列化和反序列化。
- 该工厂类维护了
7.3 初始化 BeanDefinition 读取器
-
使用
XmlBeanDefinitionReader
该读取器专门用于解析 XML 格式的BeanDefinition
,并将解析结果注册到BeanFactory
中。
源码片段:// 创建 XmlBeanDefinitionReader 并与 BeanFactory 绑定 XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory); reader.setResourceLoader(this); // 设置资源加载器
- 读取器通过回调机制与
BeanFactory
交互,将解析后的BeanDefinition
注册到工厂的BeanDefinitionMap
。 - 支持自定义
NamespaceHandler
处理复杂 XML 标签(如<context:component-scan>
)。
- 读取器通过回调机制与
7.4 加载并注册 BeanDefinition
-
核心方法
loadBeanDefinitions()
该方法完成 XML 文件的读取、解析和注册全过程。
源码流程:-
加载 XML 文件为
Document
对象// 将 Resource 转换为 DOM 文档 Document doc = doLoadDocument(inputSource, resource);
-
解析
<bean>
标签生成BeanDefinition
BeanDefinitionHolder bdHolder = parseBeanDefinitionElement(ele);
-
注册到
BeanFactory
getRegistry().registerBeanDefinition(beanName, beanDefinition);
- 解析过程中处理属性注入(如
property
标签)、作用域(scope
)、懒加载(lazy-init
)等元数据。 - 最终所有
BeanDefinition
存储在BeanFactory
的ConcurrentHashMap
中,供后续实例化使用。
-
加载 XML 文件为
流程总结
步骤 | 输入 | 输出 | 源码类/方法 |
---|---|---|---|
1. 资源定位 | XML 文件路径 |
Resource 对象 |
ClassPathXmlApplicationContext.setConfigLocations() |
2. 创建工厂 | 无 | DefaultListableBeanFactory |
DefaultListableBeanFactory 构造函数 |
3. 初始化读取器 | BeanFactory |
XmlBeanDefinitionReader |
XmlBeanDefinitionReader 初始化 |
4. 加载注册 | Resource |
注册后的 BeanDefinitionMap
|
XmlBeanDefinitionReader.loadBeanDefinitions() |
8、Bean 创建核心流程
入口方法:
AbstractApplicationContext.finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory)
作用:初始化所有非延迟加载的单例 Bean,完成 Bean 的实例化、依赖注入及缓存。
流程图与源码对照解析
8.1 触发 Bean 初始化
源码入口:
beanFactory.preInstantiateSingletons()
→ 遍历所有 Bean,对非延迟单例 Bean 执行初始化。
对应流程节点:doGetBean()
开始。
// AbstractApplicationContext.java
protected void finishBeanFactoryInitialization(...) {
beanFactory.preInstantiateSingletons(); // 触发所有非延迟单例 Bean 的初始化
}
8.2 Bean 名称解析
流程节点:转换对应的 beanName
源码实现:
处理别名、FactoryBean 前缀(如 &
符号),返回规范化的 Bean 名称。
// AbstractBeanFactory.java
protected String transformedBeanName(String name) {
return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}
8.3 单例 Bean 缓存检查
流程节点:尝试从缓存中加载单例
源码实现:
DefaultSingletonBeanRegistry.getSingleton()
优先从三级缓存中获取 Bean。
// DefaultSingletonBeanRegistry.java
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName); // 一级缓存(完整 Bean)
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName); // 二级缓存(未初始化完成的 Bean)
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); // 三级缓存(ObjectFactory)
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
return singletonObject;
}
8.4 处理原型模式依赖检查
流程节点:原型模式依赖检查
源码逻辑:
若当前 Bean 是原型模式且存在循环依赖,直接抛出异常(原型 Bean 不支持循环依赖)。
// AbstractBeanFactory.java
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
8.5 委派父容器加载
流程节点:检测是否到父类工厂加载 Bean
源码实现:
若当前容器不存在 Bean 定义,尝试从父容器加载。
// AbstractBeanFactory.java
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
return parentBeanFactory.getBean(name); // 委派父容器加载
}
8.6 处理 Bean 依赖
流程节点:寻找 Bean 的依赖
→ 存在依赖的 Bean
源码逻辑:
解析 @DependsOn
注解或 XML 配置的依赖关系,优先初始化依赖的 Bean。
// AbstractBeanFactory.java
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
getBean(dep); // 递归加载依赖的 Bean
}
}
8.7 创建 Bean 实例
流程节点:创建 Bean 实例
源码实现:
通过反射或工厂方法实例化 Bean,并执行依赖注入(属性填充)。
// AbstractAutowireCapableBeanFactory.java
protected Object createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
// 反射实例化(默认无参构造器)
return BeanUtils.instantiateClass(constructorToUse);
}
// 填充属性(依赖注入)
protected void populateBean(...) {
for (PropertyValue pv : mbd.getPropertyValues().getPropertyValues()) {
applyPropertyValue(beanName, mbd, bw, pv);
}
}
8.8 单例 Bean 缓存
流程节点:放入缓存池
源码实现:
将完整初始化的单例 Bean 存储在一级缓存 singletonObjects
中。
// DefaultSingletonBeanRegistry.java
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject); // 一级缓存
this.singletonFactories.remove(beanName); // 清理三级缓存
this.earlySingletonObjects.remove(beanName); // 清理二级缓存
}
}
流程总结
流程图节点 | 源码类/方法 | 关键操作 |
---|---|---|
doGetBean | AbstractBeanFactory.doGetBean() |
入口方法,触发整个流程 |
缓存加载 | DefaultSingletonBeanRegistry.getSingleton() |
三级缓存解决循环依赖 |
依赖处理 |
AbstractBeanFactory.getBean() 递归调用 |
优先初始化依赖 Bean |
实例化 | AbstractAutowireCapableBeanFactory.createBeanInstance() |
反射或工厂方法创建对象 |
属性填充 | AbstractAutowireCapableBeanFactory.populateBean() |
依赖注入(Setter/字段注入) |
缓存单例 | DefaultSingletonBeanRegistry.addSingleton() |
存储到一级缓存 |
循环依赖处理机制
Spring 通过 三级缓存 解决单例 Bean 的循环依赖问题:
-
一级缓存:
singletonObjects
→ 存储完全初始化后的单例 Bean。 -
二级缓存:
earlySingletonObjects
→ 存储提前暴露的未完成初始化的 Bean。 -
三级缓存:
singletonFactories
→ 存储生成 Bean 的 ObjectFactory(用于 AOP 代理等场景)。
源码引用说明
: AbstractApplicationContext.finishBeanFactoryInitialization()
是 Spring 容器初始化的最后一步,负责触发所有非延迟单例 Bean 的创建。
: DefaultSingletonBeanRegistry
管理单例 Bean 的缓存和生命周期。
: AbstractAutowireCapableBeanFactory
实现 Bean 的实例化、属性注入和初始化方法调用。
以下是结合您提供的流程图、文字描述及 Spring 源码的 Bean 生命周期详细解析:
9、Spring Bean 生命周期全流程
9.1 实例化阶段
9.1.1 实例化对象
-
源码入口:
AbstractAutowireCapableBeanFactory.createBeanInstance()
-
实现方式:
- 通过反射调用构造函数(无参或有参)。
- 若配置了工厂方法(如
@Bean
),则调用工厂方法生成对象。
-
关键代码:
// 反射实例化 BeanUtils.instantiateClass(constructorToUse); // 或工厂方法 factoryMethod.invoke(factoryBean, args);
9.2 属性赋值与依赖注入
9.2.1 设置对象属性
-
源码入口:
AbstractAutowireCapableBeanFactory.populateBean()
-
实现方式:
-
XML 配置:解析
<property>
标签,通过 Setter 方法或字段反射注入值。 -
注解驱动:
@Autowired
、@Value
等注解自动注入依赖。
-
XML 配置:解析
-
关键代码:
// 反射注入字段值 field.set(bean, value); // 或调用 Setter 方法 method.invoke(bean, value);
9.2.2 处理循环依赖
-
三级缓存机制:
通过DefaultSingletonBeanRegistry
的三级缓存(singletonFactories
、earlySingletonObjects
、singletonObjects
)提前暴露未完成初始化的 Bean 引用。
9.3 Aware 接口回调
源码入口:AbstractAutowireCapableBeanFactory.invokeAwareMethods()
作用:让 Bean 感知容器环境。
Aware 接口 | 触发方法 | 典型应用场景 |
---|---|---|
BeanNameAware |
setBeanName(String name) |
获取 Bean 的 ID(如日志标记)。 |
BeanFactoryAware |
setBeanFactory(BeanFactory factory) |
访问容器其他 Bean(需谨慎)。 |
ApplicationContextAware |
setApplicationContext(ApplicationContext ctx) |
获取应用上下文(如读取配置文件)。 |
示例代码:
public class UserService implements BeanNameAware {
private String beanName;
@Override
public void setBeanName(String name) {
this.beanName = name; // 获取 Bean 的 ID
}
}
9.4 初始化阶段
9.4.1 BeanPostProcessor 前置处理
-
源码入口:
AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization()
- 作用:在 Bean 初始化前插入逻辑(如修改 Bean 属性)。
-
典型应用:
-
@PostConstruct
注解处理(由CommonAnnotationBeanPostProcessor
实现)。 - AOP 代理对象的生成(由
AbstractAutoProxyCreator
处理)。
-
示例代码:
// 自定义 BeanPostProcessor
public class CustomBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
// 在初始化前修改 Bean
return bean;
}
}
9.4.2 执行初始化方法
源码入口:AbstractAutowireCapableBeanFactory.invokeInitMethods()
触发顺序:
-
InitializingBean 接口:
if (bean instanceof InitializingBean) { ((InitializingBean) bean).afterPropertiesSet(); // 调用接口方法 }
-
自定义 init-method:
解析 XML 中<bean init-method="init">
或@Bean(initMethod = "init")
指定的方法。
示例:
public class OrderService implements InitializingBean {
@Override
public void afterPropertiesSet() { // 接口方法
// 初始化逻辑
}
public void customInit() { // 自定义方法
// 另一种初始化方式
}
}
9.4.3 BeanPostProcessor 后置处理
-
源码入口:
AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization()
- 作用:在初始化后进一步处理 Bean。
-
典型应用:
- AOP 动态代理的生成(如
AnnotationAwareAspectJAutoProxyCreator
)。 - 缓存增强(如
CacheAspectSupport
)。
- AOP 动态代理的生成(如
示例:
// 代理生成伪代码
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (needProxy(bean)) {
return createProxy(bean); // 生成代理对象
}
return bean;
}
9.5 使用阶段
-
单例 Bean:存储在
DefaultSingletonBeanRegistry.singletonObjects
中,供全局复用。 -
原型 Bean:每次
getBean()
生成新对象,不缓存。
源码验证:
// 单例 Bean 缓存验证
Object cachedBean = beanFactory.getSingleton("userService");
assert cachedBean != null;
9.6 销毁阶段
触发条件:容器关闭时(ApplicationContext.close()
)。
执行顺序:
-
DisposableBean 接口:
if (bean instanceof DisposableBean) { ((DisposableBean) bean).destroy(); // 接口方法 }
-
自定义 destroy-method:
解析 XML 中<bean destroy-method="destroy">
或@Bean(destroyMethod = "destroy")
指定的方法。
源码入口:DisposableBeanAdapter.destroy()
示例:
public class PaymentService implements DisposableBean {
@Override
public void destroy() { // 接口方法
// 释放资源
}
public void customDestroy() { // 自定义方法
// 另一种销毁逻辑
}
}
生命周期总结
阶段 | 关键操作 | 源码方法 |
---|---|---|
实例化 | 反射/工厂方法创建对象 | createBeanInstance() |
属性注入 | 填充依赖属性 | populateBean() |
Aware 回调 | 设置容器上下文 | invokeAwareMethods() |
初始化前 | BeanPostProcessor 前置处理 | applyBeanPostProcessorsBeforeInitialization() |
初始化 |
InitializingBean.afterPropertiesSet() 或 init-method
|
invokeInitMethods() |
初始化后 | BeanPostProcessor 后置处理 | applyBeanPostProcessorsAfterInitialization() |
使用中 | 单例缓存或原型创建 |
getSingleton() / createBean()
|
销毁 |
DisposableBean.destroy() 或 destroy-method
|
destroyBean() |
扩展点与应用场景
-
BeanPostProcessor
- 修改 Bean 属性:在初始化前后调整 Bean 状态。
- 生成代理对象:AOP 的核心实现机制。
-
Aware 接口
- 获取容器元数据:如 Bean ID、ApplicationContext。
-
InitializingBean/DisposableBean
- 统一生命周期管理:适用于框架级组件(如数据源、线程池)。
-
init-method/destroy-method
- 解耦自定义逻辑:避免代码与 Spring 接口强绑定。
通过上述流程,Spring 提供了高度可扩展的 Bean 生命周期管理机制,开发者可以在不同阶段插入自定义逻辑,满足复杂业务需求。
10、Spring 如何解决循环依赖问题的?
这一机制的核心源码逻辑位于DefaultSingletonBeanRegistry
类中。以下结合源码详细解析其实现原理:
10.1 循环依赖的三种场景与限制
-
构造器注入循环依赖
无法解决。在实例化阶段就需要依赖注入,此时Bean尚未放入缓存,直接抛出BeanCurrentlyInCreationException
异常。 -
Setter/字段注入循环依赖
通过三级缓存机制解决,适用于单例Bean。 -
原型(Prototype)作用域循环依赖
无法解决,Spring会直接报错。
10.2 三级缓存的源码实现
在DefaultSingletonBeanRegistry
类中定义了三层缓存:
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); // 一级缓存(完整Bean)
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); // 二级缓存(早期引用)
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); // 三级缓存(Bean工厂)
核心方法 getSingleton()
的逻辑 :
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName); // 查一级缓存
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName); // 查二级缓存
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); // 查三级缓存
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject(); // 通过工厂创建早期引用
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName); // 升级到二级缓存
}
}
}
}
return singletonObject;
}
10.3 解决流程的源码步骤(以BeanA和BeanB循环依赖为例)
-
BeanA实例化
调用构造函数创建BeanA实例,通过doCreateBean()
方法将BeanA的工厂(ObjectFactory
)存入三级缓存:addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
BeanA属性填充
在populateBean()
阶段检测到依赖BeanB,触发getBean(B)
,进入BeanB的创建流程。BeanB实例化与属性填充
BeanB同样经过实例化后存入三级缓存。当BeanB需要注入BeanA时,通过getSingleton(A)
从三级缓存获取BeanA的工厂,生成早期引用并存入二级缓存。BeanB初始化完成
完成属性注入后,BeanB被放入一级缓存(singletonObjects
),同时清理二级缓存。BeanA完成初始化
此时BeanB已存在,BeanA完成属性注入并存入一级缓存,清理二级缓存。
10.4 三级缓存的设计原因
-
一级缓存(singletonObjects)
存储完全初始化的Bean,保证单例的唯一性。 -
二级缓存(earlySingletonObjects)
临时存放未完成属性注入的早期引用,避免重复创建工厂对象。 -
三级缓存(singletonFactories)
存储Bean工厂(ObjectFactory
),解决AOP代理对象的依赖问题。代理对象的创建需要延迟到属性注入阶段,通过工厂动态生成代理实例。
示例场景:若BeanA需要被AOP代理,三级缓存中的工厂会返回代理对象而非原始对象,保证依赖注入的一致性。
10.5 源码中的关键扩展点
-
BeanPostProcessor
在postProcessBeforeInitialization
和postProcessAfterInitialization
阶段介入Bean生命周期,例如@Autowired
注解的处理依赖AutowiredAnnotationBeanPostProcessor
。 -
InstantiationAwareBeanPostProcessor
允许在实例化前后进行干预(如生成代理对象),直接影响三级缓存的逻辑。
10.6 其他解决方案与限制
-
@Lazy注解
延迟依赖的实际初始化,通过代理对象临时解决构造器注入的循环依赖(但非根治)。 -
代码重构
通过接口抽象、依赖倒置等方式消除循环依赖,是更推荐的根本解决方案。
总结
Spring的三级缓存机制通过提前暴露半成品Bean和工厂动态代理,巧妙解决了Setter注入的循环依赖问题。其源码实现核心在于DefaultSingletonBeanRegistry
中的缓存操作和AbstractAutowireCapableBeanFactory
中的Bean生命周期管理。理解这一机制对排查Bean初始化问题、优化大型应用启动性能有重要意义。
11、Filter、Interceptor及Aspect的区别?
在 Java Web 开发中,过滤器(Filter)、拦截器(Interceptor) 和 切面(Aspect) 是三种不同层次的请求处理机制。它们的定位、执行顺序和源码实现存在显著差异,以下是结合源码和示例的详细解析:
11.1 三者的定位与作用范围
-
过滤器(Filter)
- 定位:基于 Servlet 规范,作用于 Web 容器层面(如 Tomcat),对 所有 HTTP 请求和响应 进行全局处理。
- 作用范围:处理请求的预处理(如编码设置、安全校验)和后处理(如响应压缩)。
-
示例:
@Component public class TimeFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("过滤器开始"); chain.doFilter(request, response); // 放行请求 System.out.println("过滤器结束"); } }
-
拦截器(Interceptor)
- 定位:基于 Spring MVC 框架,作用于 Controller 方法调用前后,与 Spring 上下文绑定。
- 作用范围:拦截 Spring MVC 的请求,可获取 Controller 类和方法信息,但无法直接获取方法参数。
-
示例:
@Component public class TimeInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { System.out.println("拦截器 preHandle"); return true; // 若返回 false,后续流程终止 } }
-
切面(Aspect)
- 定位:基于 Spring AOP,作用于 方法执行层面,通过动态代理实现横切逻辑(如日志、事务)。
- 作用范围:拦截 Spring 管理的 Bean 方法,可获取方法参数和返回值,但无法直接操作 HTTP 请求/响应对象。
-
示例:
@Aspect @Component public class TimeAspect { @Around("execution(* com.example.controller.*.*(..))") public Object logTime(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("切面开始"); Object result = joinPoint.proceed(); System.out.println("切面结束"); return result; } }
11.2 执行顺序与源码逻辑
总体顺序:过滤器 → 拦截器 → 切面 → Controller 方法 → 切面 → 拦截器 → 过滤器。
具体流程如下:
-
过滤器(FilterChain 调用)
-
源码入口:
FilterChain#doFilter()
,请求进入 Servlet 容器后,按过滤器注册顺序依次执行。 -
关键逻辑:
// 伪代码:FilterChainImpl 的调用链 public void doFilter(ServletRequest req, ServletResponse res) { if (index < filters.size()) { filters.get(index++).doFilter(req, res, this); // 递归调用过滤器链 } else { servlet.service(req, res); // 最终调用 Servlet(即 DispatcherServlet) } }
-
源码入口:
-
拦截器(HandlerExecutionChain 调度)
-
源码入口:
DispatcherServlet#doDispatch()
,在 Controller 方法执行前后触发拦截器方法。 -
关键逻辑:
// HandlerExecutionChain 中的处理流程 if (!mappedHandler.applyPreHandle(request, response)) return; // 执行 preHandle mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); // 执行 Controller 方法 mappedHandler.applyPostHandle(processedRequest, response, mv); // 执行 postHandle
-
源码入口:
-
切面(AOP 动态代理)
-
源码入口:通过
ProxyFactory
创建代理对象,在方法调用时触发切面逻辑。 -
关键逻辑:
// AspectJMethodBeforeAdvice 的执行 public void before(Method method, Object[] args, Object target) { invokeAdviceMethod(joinPoint, null, null); // 调用 @Before 通知 }
-
源码入口:通过
11.3 执行顺序的典型场景
以下是一个请求处理流程的完整日志示例:
过滤器开始
拦截器 preHandle
切面开始
Controller 方法执行
切面结束
拦截器 postHandle
拦截器 afterCompletion
过滤器结束
特殊场景:
-
拦截器终止流程:若拦截器的
preHandle
返回false
,则后续 切面和 Controller 方法均不会执行,直接进入过滤器的后处理。 -
AOP 异常处理:若切面或 Controller 抛出异常,会触发拦截器的
afterCompletion
方法,但不会执行postHandle
。
11.4 三者的核心区别
特性 | 过滤器(Filter) | 拦截器(Interceptor) | 切面(Aspect) |
---|---|---|---|
作用层级 | Servlet 容器 | Spring MVC 层 | Spring AOP 层 |
依赖框架 | Servlet 规范(如 Tomcat) | Spring MVC | Spring AOP |
可获取信息 | HTTP 请求/响应对象 | Controller 类和方法 | 方法参数和返回值 |
典型应用 | 跨域处理、编码设置 | 权限校验、请求日志 | 事务管理、性能监控 |
执行顺序优先级 | 最先执行 | 中间执行 | 最后执行(方法层面) |
11.5 源码中的关键设计
-
过滤器的注册机制
-
Spring Boot 中:通过
FilterRegistrationBean
或@WebFilter
注解注册,支持 URL 模式匹配。
-
Spring Boot 中:通过
-
拦截器的配置扩展
- 通过
WebMvcConfigurer#addInterceptors()
注册,支持排除特定路径。
- 通过
-
AOP 的动态代理策略
- 默认使用 JDK 动态代理(接口)或 CGLIB(类),通过
@EnableAspectJAutoProxy
配置。
- 默认使用 JDK 动态代理(接口)或 CGLIB(类),通过
11.6 如何选择?
- 需要处理 HTTP 原始数据(如修改请求头)→ 选择 过滤器。
- 需要与 Spring 上下文交互(如依赖注入 Service)→ 选择 拦截器或切面。
- 需要拦截具体方法逻辑(如记录方法参数)→ 选择 切面。
通过合理组合这三种机制,可以实现从全局到细粒度的完整请求处理链路。
12、Spring MVC 原理与机制详解(结合源码)
Spring MVC 是基于 Servlet 的 Web 框架,其核心围绕 DispatcherServlet 展开,通过组件化设计实现请求处理的分层与解耦。以下结合源码解析其核心机制:
12.1 核心组件与执行流程
Spring MVC 的请求处理流程可概括为 “前端控制器 → 处理器映射 → 处理器适配 → 视图解析”,具体流程如下:
-
DispatcherServlet 入口:
doDispatch()
方法
所有请求由DispatcherServlet
的doDispatch()
方法统一调度,源码逻辑如下(简化):
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 1. 获取处理器执行链(包含拦截器)
HandlerExecutionChain mappedHandler = getHandler(request);
// 2. 获取处理器适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 3. 执行拦截器 preHandle()
if (!mappedHandler.applyPreHandle(request, response)) return;
// 4. 调用 Controller 方法并返回 ModelAndView
ModelAndView mv = ha.handle(request, response, mappedHandler.getHandler());
// 5. 执行拦截器 postHandle()
mappedHandler.applyPostHandle(request, response, mv);
// 6. 渲染视图
processDispatchResult(request, response, mappedHandler, mv);
}
核心步骤:
-
getHandler()
:通过HandlerMapping
根据 URL 查找匹配的处理器(如@RequestMapping
方法)。 -
getHandlerAdapter()
:适配不同类型的处理器(如@Controller
或HttpRequestHandler
)。 - 拦截器执行:支持在请求前后插入自定义逻辑(如权限校验)。
12.2 关键组件源码解析
- HandlerMapping(处理器映射)
- 作用:将请求 URL 映射到具体的 Controller 方法。
-
实现类:
RequestMappingHandlerMapping
解析@RequestMapping
注解,生成HandlerMethod
对象。 -
源码入口:
启动时扫描所有// RequestMappingHandlerMapping 类 protected HandlerMethod getHandlerInternal(HttpServletRequest request) { // 根据请求路径匹配 Controller 方法 return lookupHandlerMethod(request); }
@Controller
方法,构建 URL 与方法映射表。
- HandlerAdapter(处理器适配器)
-
作用:统一调用不同处理器(如反射调用
@Controller
方法)。 -
核心实现:
RequestMappingHandlerAdapter
。 -
执行流程:
使用 参数解析器(如// RequestMappingHandlerAdapter 类 protected ModelAndView invokeHandlerMethod(...) { // 参数解析器绑定请求参数 ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod); invocableMethod.setArgumentResolvers(this.argumentResolvers); // 反射调用方法 invocableMethod.invokeAndHandle(webRequest, mavContainer); return mavContainer.getModelAndView(); }
@RequestParam
)和 返回值处理器(如@ResponseBody
)完成方法调用。
- ViewResolver(视图解析器)
-
作用:将逻辑视图名(如
"home"
)解析为实际视图(如 JSP 或 Thymeleaf)。 -
默认实现:
InternalResourceViewResolver
解析 JSP 视图。 -
源码入口:
支持多视图技术(如 JSON、HTML)的扩展。// AbstractCachingViewResolver 类 public View resolveViewName(String viewName, Locale locale) throws Exception { // 根据视图名查找 View 对象 View view = createView(viewName, locale); return view; }
12.3 扩展机制与设计思想
-
组件初始化:
initStrategies()
在DispatcherServlet
初始化时,通过initStrategies()
方法加载九大核心组件:
protected void initStrategies(ApplicationContext context) {
initHandlerMappings(context); // 初始化 HandlerMapping
initHandlerAdapters(context); // 初始化 HandlerAdapter
initViewResolvers(context); // 初始化 ViewResolver
// 其他组件(如异常处理器、文件上传解析器)
}
所有组件均可通过 Spring 配置自定义实现,体现 开闭原则。
- 拦截器链:
HandlerExecutionChain
-
执行顺序:
preHandle()
→ Controller 方法 →postHandle()
→afterCompletion()
。 -
源码逻辑:
拦截器链支持请求处理的横向切面逻辑。// HandlerExecutionChain 类 boolean applyPreHandle(...) { for (int i = 0; i < interceptors.length; i++) { if (!interceptors[i].preHandle(request, response, handler)) return false; } return true; }
12.4 总结与面试回答要点
- 核心流程:DispatcherServlet 统一调度,通过 HandlerMapping 查找处理器,HandlerAdapter 执行方法,ViewResolver 渲染视图。
-
关键组件:
- HandlerMapping:URL 到方法的映射。
- HandlerAdapter:统一调用方法,支持参数解析。
- ViewResolver:逻辑视图到实际视图的转换。
-
扩展性:通过组件接口(如
HandlerInterceptor
)支持自定义扩展。 -
源码亮点:
doDispatch()
方法的核心调度逻辑、RequestMappingHandlerAdapter
的反射调用机制。
示例回答:
“Spring MVC 的核心是 DispatcherServlet,它通过 doDispatch()
方法协调各组件处理请求。首先,HandlerMapping 根据 URL 找到对应的 Controller 方法;接着 HandlerAdapter 使用参数解析器绑定请求参数并反射调用方法;最后,ViewResolver 将逻辑视图转换为实际视图。整个过程通过拦截器链实现横切逻辑,组件设计高度解耦,支持灵活扩展。”