Spring 必知必会

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 容器接管了这一过程,由容器负责对象的创建、生命周期管理和依赖注入(称为“反转”)。

关键点

  1. 控制权的反转
    传统方式由程序主动创建依赖对象(如 new B()),而 IoC 容器负责对象的创建和资源管理。这种反转降低了代码的耦合度,使得模块间依赖关系更灵活。

  2. 解决的问题

    • 耦合度高:传统代码中,对象直接依赖具体实现类,导致修改依赖时需改动多处代码。
    • 可测试性差:依赖硬编码使得单元测试难以模拟依赖对象。
  3. 实现方式
    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)。
  • 关键对象
    • BeanDefinition:描述 Bean 的类名、作用域、依赖关系等元数据。
    • BeanDefinitionRegistry:存储所有 Bean 定义的注册中心。

6.2 Bean 的创建(延迟初始化)
  • 触发条件:第一次调用 getBean() 请求某个 Bean 时触发创建。
  • 创建过程
    1. 根据 BeanDefinition 中的类信息(如 class="com.example.UserService")通过反射实例化对象。
    2. 填充依赖属性(如通过 Setter 或构造函数注入其他 Bean)。
    3. 调用初始化方法(如 @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 注册表 ResourceBeanDefinitionReader
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 文件的读取、解析和注册全过程。
    源码流程
    1. 加载 XML 文件为 Document 对象
      // 将 Resource 转换为 DOM 文档
      Document doc = doLoadDocument(inputSource, resource);
      
    2. 解析 <bean> 标签生成 BeanDefinition
      BeanDefinitionHolder bdHolder = parseBeanDefinitionElement(ele);
      
    3. 注册到 BeanFactory
      getRegistry().registerBeanDefinition(beanName, beanDefinition);
      
    关键点
    • 解析过程中处理属性注入(如 property 标签)、作用域(scope)、懒加载(lazy-init)等元数据。
    • 最终所有 BeanDefinition 存储在 BeanFactoryConcurrentHashMap 中,供后续实例化使用。

流程总结
步骤 输入 输出 源码类/方法
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 的循环依赖问题:

  1. 一级缓存singletonObjects → 存储完全初始化后的单例 Bean。
  2. 二级缓存earlySingletonObjects → 存储提前暴露的未完成初始化的 Bean。
  3. 三级缓存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 等注解自动注入依赖。
  • 关键代码
    // 反射注入字段值
    field.set(bean, value);
    // 或调用 Setter 方法
    method.invoke(bean, value);
    

9.2.2 处理循环依赖

  • 三级缓存机制
    通过 DefaultSingletonBeanRegistry 的三级缓存(singletonFactoriesearlySingletonObjectssingletonObjects)提前暴露未完成初始化的 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()
触发顺序

  1. InitializingBean 接口
    if (bean instanceof InitializingBean) {
        ((InitializingBean) bean).afterPropertiesSet(); // 调用接口方法
    }
    
  2. 自定义 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)。

示例

// 代理生成伪代码
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())。
执行顺序

  1. DisposableBean 接口
    if (bean instanceof DisposableBean) {
        ((DisposableBean) bean).destroy(); // 接口方法
    }
    
  2. 自定义 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()

扩展点与应用场景
  1. BeanPostProcessor

    • 修改 Bean 属性:在初始化前后调整 Bean 状态。
    • 生成代理对象:AOP 的核心实现机制。
  2. Aware 接口

    • 获取容器元数据:如 Bean ID、ApplicationContext。
  3. InitializingBean/DisposableBean

    • 统一生命周期管理:适用于框架级组件(如数据源、线程池)。
  4. init-method/destroy-method

    • 解耦自定义逻辑:避免代码与 Spring 接口强绑定。

通过上述流程,Spring 提供了高度可扩展的 Bean 生命周期管理机制,开发者可以在不同阶段插入自定义逻辑,满足复杂业务需求。

10、Spring 如何解决循环依赖问题的?

这一机制的核心源码逻辑位于DefaultSingletonBeanRegistry类中。以下结合源码详细解析其实现原理:


10.1 循环依赖的三种场景与限制
  1. 构造器注入循环依赖
    无法解决。在实例化阶段就需要依赖注入,此时Bean尚未放入缓存,直接抛出BeanCurrentlyInCreationException异常。
  2. Setter/字段注入循环依赖
    通过三级缓存机制解决,适用于单例Bean。
  3. 原型(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循环依赖为例)
  1. BeanA实例化
    调用构造函数创建BeanA实例,通过doCreateBean()方法将BeanA的工厂(ObjectFactory)存入三级缓存:

    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    
  2. BeanA属性填充
    populateBean()阶段检测到依赖BeanB,触发getBean(B),进入BeanB的创建流程。

  3. BeanB实例化与属性填充
    BeanB同样经过实例化后存入三级缓存。当BeanB需要注入BeanA时,通过getSingleton(A)从三级缓存获取BeanA的工厂,生成早期引用并存入二级缓存。

  4. BeanB初始化完成
    完成属性注入后,BeanB被放入一级缓存(singletonObjects),同时清理二级缓存。

  5. BeanA完成初始化
    此时BeanB已存在,BeanA完成属性注入并存入一级缓存,清理二级缓存。


10.4 三级缓存的设计原因
  1. 一级缓存(singletonObjects)
    存储完全初始化的Bean,保证单例的唯一性。
  2. 二级缓存(earlySingletonObjects)
    临时存放未完成属性注入的早期引用,避免重复创建工厂对象。
  3. 三级缓存(singletonFactories)
    存储Bean工厂(ObjectFactory),解决AOP代理对象的依赖问题。代理对象的创建需要延迟到属性注入阶段,通过工厂动态生成代理实例。

示例场景:若BeanA需要被AOP代理,三级缓存中的工厂会返回代理对象而非原始对象,保证依赖注入的一致性。


10.5 源码中的关键扩展点
  • BeanPostProcessor
    postProcessBeforeInitializationpostProcessAfterInitialization阶段介入Bean生命周期,例如@Autowired注解的处理依赖AutowiredAnnotationBeanPostProcessor
  • InstantiationAwareBeanPostProcessor
    允许在实例化前后进行干预(如生成代理对象),直接影响三级缓存的逻辑。

10.6 其他解决方案与限制
  1. @Lazy注解
    延迟依赖的实际初始化,通过代理对象临时解决构造器注入的循环依赖(但非根治)。
  2. 代码重构
    通过接口抽象、依赖倒置等方式消除循环依赖,是更推荐的根本解决方案。

总结

Spring的三级缓存机制通过提前暴露半成品Bean工厂动态代理,巧妙解决了Setter注入的循环依赖问题。其源码实现核心在于DefaultSingletonBeanRegistry中的缓存操作和AbstractAutowireCapableBeanFactory中的Bean生命周期管理。理解这一机制对排查Bean初始化问题、优化大型应用启动性能有重要意义。

11、Filter、Interceptor及Aspect的区别?

在 Java Web 开发中,过滤器(Filter)拦截器(Interceptor)切面(Aspect) 是三种不同层次的请求处理机制。它们的定位、执行顺序和源码实现存在显著差异,以下是结合源码和示例的详细解析:


11.1 三者的定位与作用范围
  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("过滤器结束");
          }
      }
      
  2. 拦截器(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,后续流程终止
          }
      }
      
  3. 切面(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 方法 → 切面 → 拦截器 → 过滤器
具体流程如下:

  1. 过滤器(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)
          }
      }
      
  2. 拦截器(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
      
  3. 切面(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 源码中的关键设计
  1. 过滤器的注册机制
    • Spring Boot 中:通过 FilterRegistrationBean@WebFilter 注解注册,支持 URL 模式匹配。
  2. 拦截器的配置扩展
    • 通过 WebMvcConfigurer#addInterceptors() 注册,支持排除特定路径。
  3. AOP 的动态代理策略
    • 默认使用 JDK 动态代理(接口)或 CGLIB(类),通过 @EnableAspectJAutoProxy 配置。

11.6 如何选择?
  • 需要处理 HTTP 原始数据(如修改请求头)→ 选择 过滤器
  • 需要与 Spring 上下文交互(如依赖注入 Service)→ 选择 拦截器或切面
  • 需要拦截具体方法逻辑(如记录方法参数)→ 选择 切面

通过合理组合这三种机制,可以实现从全局到细粒度的完整请求处理链路。

12、Spring MVC 原理与机制详解(结合源码)

Spring MVC 是基于 Servlet 的 Web 框架,其核心围绕 DispatcherServlet 展开,通过组件化设计实现请求处理的分层与解耦。以下结合源码解析其核心机制:


12.1 核心组件与执行流程

Spring MVC 的请求处理流程可概括为 “前端控制器 → 处理器映射 → 处理器适配 → 视图解析”,具体流程如下:

  1. DispatcherServlet 入口:doDispatch() 方法
    所有请求由 DispatcherServletdoDispatch() 方法统一调度,源码逻辑如下(简化):
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():适配不同类型的处理器(如 @ControllerHttpRequestHandler)。
  • 拦截器执行:支持在请求前后插入自定义逻辑(如权限校验)。

12.2 关键组件源码解析
  1. HandlerMapping(处理器映射)
  • 作用:将请求 URL 映射到具体的 Controller 方法。
  • 实现类RequestMappingHandlerMapping 解析 @RequestMapping 注解,生成 HandlerMethod 对象。
  • 源码入口
    // RequestMappingHandlerMapping 类
    protected HandlerMethod getHandlerInternal(HttpServletRequest request) {
        // 根据请求路径匹配 Controller 方法
        return lookupHandlerMethod(request);
    }
    
    启动时扫描所有 @Controller 方法,构建 URL 与方法映射表。
  1. HandlerAdapter(处理器适配器)
  • 作用:统一调用不同处理器(如反射调用 @Controller 方法)。
  • 核心实现RequestMappingHandlerAdapter
  • 执行流程
    // RequestMappingHandlerAdapter 类
    protected ModelAndView invokeHandlerMethod(...) {
        // 参数解析器绑定请求参数
        ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
        invocableMethod.setArgumentResolvers(this.argumentResolvers);
        // 反射调用方法
        invocableMethod.invokeAndHandle(webRequest, mavContainer);
        return mavContainer.getModelAndView();
    }
    
    使用 参数解析器(如 @RequestParam)和 返回值处理器(如 @ResponseBody)完成方法调用。
  1. ViewResolver(视图解析器)
  • 作用:将逻辑视图名(如 "home")解析为实际视图(如 JSP 或 Thymeleaf)。
  • 默认实现InternalResourceViewResolver 解析 JSP 视图。
  • 源码入口
    // AbstractCachingViewResolver 类
    public View resolveViewName(String viewName, Locale locale) throws Exception {
        // 根据视图名查找 View 对象
        View view = createView(viewName, locale);
        return view;
    }
    
    支持多视图技术(如 JSON、HTML)的扩展。

12.3 扩展机制与设计思想
  1. 组件初始化:initStrategies()
    DispatcherServlet 初始化时,通过 initStrategies() 方法加载九大核心组件:
protected void initStrategies(ApplicationContext context) {
    initHandlerMappings(context);    // 初始化 HandlerMapping
    initHandlerAdapters(context);    // 初始化 HandlerAdapter
    initViewResolvers(context);      // 初始化 ViewResolver
    // 其他组件(如异常处理器、文件上传解析器)
}

所有组件均可通过 Spring 配置自定义实现,体现 开闭原则

  1. 拦截器链: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 总结与面试回答要点
  1. 核心流程:DispatcherServlet 统一调度,通过 HandlerMapping 查找处理器,HandlerAdapter 执行方法,ViewResolver 渲染视图。
  2. 关键组件
    • HandlerMapping:URL 到方法的映射。
    • HandlerAdapter:统一调用方法,支持参数解析。
    • ViewResolver:逻辑视图到实际视图的转换。
  3. 扩展性:通过组件接口(如 HandlerInterceptor)支持自定义扩展。
  4. 源码亮点doDispatch() 方法的核心调度逻辑、RequestMappingHandlerAdapter 的反射调用机制。

示例回答
“Spring MVC 的核心是 DispatcherServlet,它通过 doDispatch() 方法协调各组件处理请求。首先,HandlerMapping 根据 URL 找到对应的 Controller 方法;接着 HandlerAdapter 使用参数解析器绑定请求参数并反射调用方法;最后,ViewResolver 将逻辑视图转换为实际视图。整个过程通过拦截器链实现横切逻辑,组件设计高度解耦,支持灵活扩展。”

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容