Skip to content

源码解读

为什么需要阅读源码

在日常开发中,我们通过文档和教程能够快速上手一个框架,但这只是"知其然"。当遇到以下场景时,源码阅读就变得不可或缺:

  • 排查诡异 Bug:框架表现不符合预期,日志信息不足,文档没有覆盖边缘场景。此时只有深入源码,才能理解框架的真实行为逻辑。
  • 性能调优:框架的默认配置不一定适合你的业务场景。理解内部实现后,才能精准调整参数、替换组件,而不是盲目"调参"。
  • 学习设计模式:优秀框架是设计模式的最佳实践场。Spring 中随处可见模板方法、策略、观察者等模式,阅读源码比看任何设计模式教材都更深刻。
  • 技术面试:高级岗位面试中,"你看过哪些框架源码"几乎是必问题。能讲清楚一个框架的核心流程,远比背诵八股文更有说服力。
  • 提升编码能力:阅读顶级开源项目的代码,学习命名规范、异常处理、并发控制、扩展性设计,潜移默化地提升自己的代码品味。
阅读层级目标典型产出
第一层:会用通过文档和示例完成功能开发功能实现
第二层:理解看懂核心流程和关键设计技术分享、问题排查
第三层:改造能扩展、裁剪、优化框架定制化组件、性能优化
第四层:创造借鉴思想设计自己的框架自研框架/中间件

如何高效阅读源码

1. 明确目标,带着问题去读

不要试图从头到尾读完整份源码——那只会让你迷失。每次阅读应该有明确的问题驱动,例如:

  • "Spring 是如何解决循环依赖的?"
  • "MyBatis 的 Mapper 接口为什么不需要实现类?"
  • "Spring Boot 自动配置是如何发现和加载的?"

2. 从入口开始,沿调用链追踪

以调试模式运行一个最简单的 Demo,在关键位置打断点,然后一步步跟进。这是最直观的方式:

1. 写一个最简单的测试用例
2. 在框架入口处打断点(如 Spring 的 ApplicationContext 构造)
3. Step Into 进入核心方法
4. 观察调用栈,画出调用链
5. 重点关注:关键数据结构、分支逻辑、扩展点

3. 善用 IDE 的源码导航能力

  • Ctrl+Alt+B (IDEA):查看接口/抽象方法的所有实现
  • Ctrl+H:查看类的继承层次结构
  • Ctrl+Alt+F7:查找方法/字段的所有使用位置
  • Diagrams 插件:生成类图、调用图
  • Bookmarks:标记关键类和方法,方便反复跳转

4. 画图辅助理解

复杂的调用链和类关系,用文字描述远不如一张图清晰。推荐做法:

  • 时序图:记录关键方法的调用顺序(谁调用了谁,传了什么参数)
  • 类图:记录核心类之间的依赖和继承关系
  • 流程图:记录某个核心流程的分支逻辑

5. 抓住主干,忽略细节

框架源码通常有大量边界处理、日志、异常处理代码。第一次阅读时大胆跳过:

java
// 第一次阅读时可以直接跳过这类代码
if (logger.isTraceEnabled()) {
    logger.trace("...");
}
// 以及各种 null check、参数校验等

重点关注:核心数据结构 + 核心流程 + 扩展点接口

6. 版本选择策略

策略优点缺点
读最新版学到最新设计,面试加分代码量大,复杂度高
读早期版本(如 Spring 1.x)代码量小,核心思想清晰部分设计已过时
对比阅读理解演进原因耗时较长

建议:先读早期版本理解核心思想,再对比最新版本看演进。

Spring IoC 容器源码解读

Spring IoC 是整个 Spring 生态的基石,理解它等于掌握了 Spring 的"任督二脉"。

核心类层次结构

BeanFactory (接口)
  └── HierarchicalBeanFactory
  └── ListableBeanFactory
  └── AutowireCapableBeanFactory
      └── ConfigurableBeanFactory
          └── AbstractBeanFactory
              └── AbstractAutowireCapableBeanFactory
                  └── DefaultListableBeanFactory
                      └── XmlBeanFactory (已废弃)

ApplicationContext (接口)
  └── ConfigurableApplicationContext
      └── AbstractApplicationContext
          └── AbstractRefreshableApplicationContext
              └── AbstractRefreshableConfigApplicationContext
                  └── AbstractXmlApplicationContext
                      └── ClassPathXmlApplicationContext
          └── GenericApplicationContext
              └── AnnotationConfigApplicationContext

refresh() 方法——Spring 容器启动的核心

AbstractApplicationContext.refresh() 是整个 Spring 容器初始化的入口,它定义了容器启动的 12 个步骤(模板方法模式):

java
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // 1. 准备刷新:记录启动时间,初始化属性源
        prepareRefresh();

        // 2. 获取 BeanFactory:如果是可刷新容器则刷新,否则新建
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // 3. 准备 BeanFactory:设置类加载器、添加后置处理器等
        prepareBeanFactory(beanFactory);

        try {
            // 4. 允许子类对 BeanFactory 进行后置处理(模板方法)
            postProcessBeanFactory(beanFactory);

            // 5. 调用 BeanFactoryPostProcessor(核心扩展点)
            invokeBeanFactoryPostProcessors(beanFactory);

            // 6. 注册 BeanPostProcessor
            registerBeanPostProcessors(beanFactory);

            // 7. 初始化 MessageSource(国际化)
            initMessageSource();

            // 8. 初始化事件广播器
            initApplicationEventMulticaster();

            // 9. 模板方法:留给子类初始化其他特殊 Bean
            onRefresh();

            // 10. 注册事件监听器
            registerListeners();

            // 11. 实例化所有非懒加载的单例 Bean
            finishBeanFactoryInitialization(beanFactory);

            // 12. 完成刷新:发布 ContextRefreshedEvent 事件
            finishRefresh();
        } catch (BeansException ex) {
            // 销毁已创建的 Bean
            destroyBeans();
            cancelRefresh(ex);
            throw ex;
        }
    }
}

BeanDefinition 加载流程

以 XML 配置为例,Bean 定义的加载过程如下:

1. XmlBeanDefinitionReader 读取 XML 配置文件
2. 委托给 DefaultBeanDefinitionDocumentReader 解析 <beans> 元素
3. 遍历每个 <bean> 元素,委托给 BeanDefinitionParserDelegate 解析
4. 生成 BeanDefinitionHolder(包含 BeanDefinition + beanName + aliases)
5. 注册到 BeanDefinitionRegistry(即 DefaultListableBeanFactory 的 beanDefinitionMap)
java
// DefaultListableBeanFactory 中 BeanDefinition 的存储结构
private final Map<String, BeanDefinition> beanDefinitionMap = 
    new ConcurrentHashMap<>(256);

依赖注入流程

当调用 getBean() 时,Spring 的依赖注入流程如下:

java
// AbstractBeanFactory.doGetBean() 简化版
protected <T> T doGetBean(String name, Class<T> requiredType, 
                          Object[] args, boolean typeCheckOnly) {
    // 1. 转换 beanName(处理别名、FactoryBean 的 & 前缀)
    String beanName = transformedBeanName(name);
    
    // 2. 尝试从单例缓存中获取(处理循环依赖的关键)
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null && args == null) {
        return (T) getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }
    
    // 3. 检查是否存在原型循环依赖
    if (isPrototypeCurrentlyInCreation(beanName)) {
        throw new BeanCurrentlyInCreationException(beanName);
    }
    
    // 4. 如果当前容器没有,去父容器查找
    BeanFactory parentBeanFactory = getParentBeanFactory();
    if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
        return parentBeanFactory.getBean(name, requiredType);
    }
    
    // 5. 合并 BeanDefinition(处理继承)
    RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
    
    // 6. 先实例化依赖的 Bean(depends-on)
    String[] dependsOn = mbd.getDependsOn();
    if (dependsOn != null) {
        for (String dep : dependsOn) {
            registerDependentBean(dep, beanName);
            getBean(dep);  // 递归创建依赖的 Bean
        }
    }
    
    // 7. 创建 Bean 实例
    if (mbd.isSingleton()) {
        sharedInstance = getSingleton(beanName, () -> {
            return createBean(beanName, mbd, args);
        });
    } else if (mbd.isPrototype()) {
        // 原型模式,每次新建
        sharedInstance = createBean(beanName, mbd, args);
    } else {
        // 其他 Scope(request、session 等)
        // ...
    }
    
    return (T) getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

循环依赖解决——三级缓存机制

Spring 通过三级缓存解决了单例 Bean 的构造器注入循环依赖问题:

java
// DefaultSingletonBeanRegistry 中的三级缓存
public class DefaultSingletonBeanRegistry {
    // 一级缓存:存放完全初始化好的 Bean
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    
    // 二级缓存:存放提前暴露的早期 Bean 引用(未完成属性注入)
    private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
    
    // 三级缓存:存放 Bean 的 ObjectFactory(用于生成代理对象)
    private final Map<String, ObjectFactory<?>> singletonFactories = new ConcurrentHashMap<>(16);
}

循环依赖解决流程:

A 创建 → 实例化 A → 将 A 的 ObjectFactory 放入三级缓存 
→ A 依赖 B → 创建 B → 实例化 B → 将 B 的 ObjectFactory 放入三级缓存
→ B 依赖 A → 从三级缓存获取 A 的 ObjectFactory → 生成 A 的早期引用(可能是代理)
→ 将 A 的早期引用放入二级缓存 → B 完成注入 A → B 初始化完成
→ B 放入一级缓存 → A 完成注入 B → A 初始化完成 → A 放入一级缓存

注意:Spring 只能解决单例 setter 注入的循环依赖,无法解决构造器注入的循环依赖原型 Bean 的循环依赖

Spring AOP 源码解读

核心概念与类关系

Spring AOP 基于动态代理实现,核心类如下:

AopProxy (接口)
  ├── JdkDynamicAopProxy     → 基于 JDK 动态代理(要求实现接口)
  └── CglibAopProxy          → 基于 CGLIB 字节码增强(无接口时使用)

ProxyFactory                 → 创建代理对象的工厂
AdvisedSupport               → 封装 AOP 配置(目标对象、拦截器链等)
Advisor                      → 切面 = Advice + Pointcut
  └── PointcutAdvisor
      └── AspectJPointcutAdvisor

AOP 代理创建流程

java
// ProxyFactory.getProxy() 简化逻辑
public Object getProxy() {
    // 根据配置选择代理方式
    AopProxyFactory aopProxyFactory = new DefaultAopProxyFactory();
    return aopProxyFactory.createAopProxy(this).getProxy();
}

// DefaultAopProxyFactory.createAopProxy()
public AopProxy createAopProxy(AdvisedSupport config) {
    if (config.isOptimize() || config.isProxyTargetClass() 
        || hasNoUserSuppliedProxyInterfaces(config)) {
        Class<?> targetClass = config.getTargetClass();
        if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
            return new JdkDynamicAopProxy(config);
        }
        return new CglibAopProxy(config);
    } else {
        return new JdkDynamicAopProxy(config);
    }
}

JdkDynamicAopProxy 核心逻辑

java
// JdkDynamicAopProxy.invoke() 简化版
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    // 1. 获取目标方法的拦截器链(Advisor 链)
    List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(
        method, targetClass);
    
    if (chain.isEmpty()) {
        // 没有匹配的切面,直接调用目标方法
        return AopUtils.invokeJoinpointUsingReflection(target, method, args);
    }
    
    // 2. 创建 MethodInvocation,执行拦截器链
    MethodInvocation invocation = new ReflectiveMethodInvocation(
        proxy, target, method, args, targetClass, chain);
    return invocation.proceed();
}

// ReflectiveMethodInvocation.proceed() 责任链模式
public Object proceed() throws Throwable {
    // 当拦截器全部执行完毕,调用目标方法
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        return invokeJoinpoint();
    }
    
    // 依次执行拦截器链中的每个拦截器
    Object interceptor = this.interceptorsAndDynamicMethodMatchers.get(
        ++this.currentInterceptorIndex);
    return ((MethodInterceptor) interceptor).invoke(this);
}

CglibAopProxy 核心逻辑

CGLIB 通过生成目标类的子类来实现代理,核心是 DynamicAdvisedInterceptor

java
// CglibAopProxy.DynamicAdvisedInterceptor.intercept() 简化版
public Object intercept(Object proxy, Method method, Object[] args, 
                        MethodProxy methodProxy) throws Throwable {
    // 获取拦截器链
    List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(
        method, targetClass);
    
    if (chain.isEmpty()) {
        // 直接调用目标方法(通过 CGLIB 的 MethodProxy)
        return methodProxy.invoke(target, args);
    }
    
    // 创建 CglibMethodInvocation 执行拦截器链
    CglibMethodInvocation invocation = new CglibMethodInvocation(
        proxy, target, method, args, targetClass, chain, methodProxy);
    return invocation.proceed();
}

Advisor 链的构建过程

java
// DefaultAdvisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice()
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
        Advised config, Method method, Class<?> targetClass) {
    
    List<Object> interceptorList = new ArrayList<>(config.getAdvisors().length);
    
    for (Advisor advisor : config.getAdvisors()) {
        if (advisor instanceof PointcutAdvisor) {
            PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
            // 检查切点是否匹配当前方法和类
            if (pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass)
                && pointcutAdvisor.getPointcut().getMethodMatcher().matches(method, targetClass)) {
                // 将 Advisor 转换为 MethodInterceptor
                MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
                interceptorList.addAll(Arrays.asList(interceptors));
            }
        }
    }
    return interceptorList;
}

JDK 动态代理 vs CGLIB 代理

对比维度JDK 动态代理CGLIB 代理
实现原理基于接口,通过 Proxy + InvocationHandler 生成代理类基于继承,通过 ASM 生成目标类的子类
要求目标类必须实现接口目标类不能是 final,方法不能是 final
性能创建代理快,执行稍慢(反射)创建代理慢(字节码生成),执行快
默认策略Spring 中目标类实现了接口则默认使用 JDK 代理Spring Boot 2.x 开始默认使用 CGLIB

MyBatis 源码解读

整体架构

MyBatis 的核心架构分为三层:

┌─────────────────────────────────────────────┐
│              接口层 (SqlSession)              │
│  提供对外的 CRUD API,供应用程序调用           │
├─────────────────────────────────────────────┤
│             核心处理层                         │
│  参数映射 → SQL 解析 → SQL 执行 → 结果映射     │
│  (Executor, StatementHandler, ResultSetHandler)│
├─────────────────────────────────────────────┤
│             基础支撑层                         │
│  连接管理、事务管理、配置加载、缓存              │
└─────────────────────────────────────────────┘

核心组件与执行流程

SqlSessionFactoryBuilder
    → build(InputStream) → XMLConfigBuilder.parse()
        → Configuration (全局配置对象)
            → SqlSessionFactory.openSession()
                → SqlSession (会话,默认 DefaultSqlSession)
                    → Executor (执行器)
                        → StatementHandler (语句处理器)
                            → ParameterHandler (参数处理器)
                            → ResultSetHandler (结果集处理器)

1. SqlSessionFactory 的构建

java
// SqlSessionFactoryBuilder.build() 简化版
public SqlSessionFactory build(InputStream inputStream) {
    // 创建 XMLConfigBuilder 解析 mybatis-config.xml
    XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, null, null);
    // 解析生成 Configuration 对象
    return build(parser.parse());
}

// XMLConfigBuilder.parse() 核心逻辑
public Configuration parse() {
    // 解析 <configuration> 节点下的各个子节点
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
}

private void parseConfiguration(XNode root) {
    // 解析 properties、settings、typeAliases、plugins
    // environments、mappers 等配置
    propertiesElement(root.evalNode("properties"));
    settingsElement(root.evalNode("settings"));
    typeAliasesElement(root.evalNode("typeAliases"));
    pluginElement(root.evalNode("plugins"));
    environmentsElement(root.evalNode("environments"));
    mapperElement(root.evalNode("mappers"));  // 关键:解析 Mapper 映射文件
}

2. Mapper 接口的动态代理

MyBatis 最巧妙的设计:Mapper 接口不需要实现类,通过 JDK 动态代理在运行时生成实现。

java
// MapperProxyFactory.newInstance()
public T newInstance(SqlSession sqlSession) {
    // MapperProxy 实现了 InvocationHandler
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return (T) Proxy.newProxyInstance(
        mapperInterface.getClassLoader(),
        new Class[] { mapperInterface },
        mapperProxy
    );
}

// MapperProxy.invoke() 核心逻辑
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    // 如果是 Object 的方法(toString、hashCode 等),直接执行
    if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
    }
    // 获取 MapperMethod 并执行
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
}

// MapperMethod.execute() 根据 SQL 类型分派
public Object execute(SqlSession sqlSession, Object[] args) {
    switch (command.getType()) {
        case INSERT:
            return sqlSession.insert(command.getName(), param);
        case UPDATE:
            return sqlSession.update(command.getName(), param);
        case DELETE:
            return sqlSession.delete(command.getName(), param);
        case SELECT:
            if (method.returnsVoid()) {
                sqlSession.select(command.getName(), param, resultHandler);
                return null;
            } else if (method.returnsMany()) {
                return sqlSession.selectList(command.getName(), param);
            } else if (method.returnsMap()) {
                return sqlSession.selectMap(command.getName(), param, ...);
            } else {
                return sqlSession.selectOne(command.getName(), param);
            }
        default:
            throw new BindingException("Unknown execution method");
    }
}

3. Executor 执行器——二级缓存与执行链

java
// Executor 接口的继承层次
Executor (接口)
  └── BaseExecutor (抽象类,模板方法模式)
        ├── SimpleExecutor     → 简单执行器,每次执行都创建新的 Statement
        ├── ReuseExecutor      → 可重用执行器,复用 Statement
        └── BatchExecutor      → 批量执行器,用于批量操作
  └── CachingExecutor (装饰器模式) → 包装 BaseExecutor,增加二级缓存

// Configuration.newExecutor() 
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    Executor executor;
    if (ExecutorType.BATCH == executorType) {
        executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
        executor = new ReuseExecutor(this, transaction);
    } else {
        executor = new SimpleExecutor(this, transaction);
    }
    // 如果开启了二级缓存,用 CachingExecutor 装饰
    if (cacheEnabled) {
        executor = new CachingExecutor(executor);
    }
    // 应用插件拦截(责任链模式)
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
}

4. StatementHandler——SQL 执行的核心

java
// StatementHandler 的继承层次
StatementHandler (接口)
  └── BaseStatementHandler (抽象类)
        ├── SimpleStatementHandler    → 处理普通 Statement
        ├── PreparedStatementHandler  → 处理 PreparedStatement(最常用)
        └── CallableStatementHandler  → 处理 CallableStatement(存储过程)

// RoutingStatementHandler(路由,根据 MappedStatement 选择具体实现)

5. MyBatis 插件机制——责任链模式

java
// InterceptorChain 维护所有插件
public class InterceptorChain {
    private final List<Interceptor> interceptors = new ArrayList<>();
    
    // 对目标对象应用所有插件,生成代理
    public Object pluginAll(Object target) {
        for (Interceptor interceptor : interceptors) {
            target = interceptor.plugin(target);
        }
        return target;
    }
}

// 自定义插件示例:SQL 执行时间统计
@Intercepts({
    @Signature(type = StatementHandler.class, method = "query", 
               args = {Statement.class, ResultHandler.class})
})
public class SqlCostInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        long start = System.currentTimeMillis();
        try {
            return invocation.proceed();
        } finally {
            long cost = System.currentTimeMillis() - start;
            System.out.println("SQL 执行耗时: " + cost + "ms");
        }
    }
}

一级缓存与二级缓存

对比维度一级缓存二级缓存
作用范围SqlSession 级别Mapper 级别(跨 SqlSession)
默认开启
实现类PerpetualCache(HashMap)PerpetualCache + 装饰器
生命周期随 SqlSession 关闭而清除随应用生命周期(可配置驱逐策略)
清除时机update/delete/insert 后自动清除commit/close 时写入,update 后清除

源码阅读实战建议

推荐阅读顺序

  1. 先读 Spring IoC:IoC 是 Spring 的核心,AOP、事务、MVC 都依赖它
  2. 再读 Spring AOP:理解代理机制和拦截器链,这是声明式事务的基础
  3. 然后读 MyBatis:相对独立,核心流程清晰,适合练手
  4. 最后读 Spring Boot 自动配置:理解 @EnableAutoConfigurationspring.factories 机制

搭建源码阅读环境

bash
# 1. Clone 源码
git clone https://github.com/spring-projects/spring-framework.git
cd spring-framework

# 2. 切换到合适的版本标签
git checkout v5.3.30

# 3. 导入 IDEA,等待 Gradle 构建完成

# 4. 在各自模块下编写测试用例,打断点调试

调试技巧

java
// 在测试用例中打断点,观察调用栈
@Test
public void testIoC() {
    // 在这里打断点,Step Into 进入 refresh()
    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    // 在这里打断点,观察 getBean() 的完整流程
    UserService userService = context.getBean(UserService.class);
    userService.doSomething();
}

面试常见问题

1. Spring 的 refresh() 方法做了哪些事?

答:refresh() 是 Spring 容器启动的核心方法,分为 12 个步骤:准备刷新上下文、获取 BeanFactory、准备 BeanFactory、BeanFactory 后置处理、调用 BeanFactoryPostProcessor、注册 BeanPostProcessor、初始化 MessageSource、初始化事件广播器、onRefresh 模板方法、注册监听器、实例化单例 Bean、完成刷新。核心是第 5 步(调用 BeanFactoryPostProcessor)和第 11 步(实例化单例 Bean)。

2. Spring 如何解决循环依赖?

答:Spring 通过三级缓存解决单例 Bean 的 setter 注入循环依赖。一级缓存 singletonObjects 存放完全初始化的 Bean,二级缓存 earlySingletonObjects 存放提前暴露的早期引用,三级缓存 singletonFactories 存放 ObjectFactory。当 A 和 B 循环依赖时,A 先实例化后将自己的 ObjectFactory 放入三级缓存,然后注入 B,B 创建时发现依赖 A,从三级缓存获取 A 的早期引用并升级到二级缓存,B 完成后 A 再从二级缓存获取 B 完成注入。构造器注入的循环依赖无法解决

3. MyBatis 的 Mapper 接口为什么不需要实现类?

答:MyBatis 使用 JDK 动态代理,在运行时通过 MapperProxy(实现了 InvocationHandler)为 Mapper 接口生成代理对象。当调用 Mapper 接口方法时,MapperProxy.invoke() 拦截调用,根据方法名找到对应的 MappedStatement(SQL 映射),然后通过 SqlSession 执行 SQL 并返回结果。

4. Spring AOP 和 AspectJ 有什么区别?

答:Spring AOP 是运行时动态代理(JDK 动态代理或 CGLIB),只能对 Spring 管理的 Bean 进行方法级别的增强;AspectJ 是编译时/类加载时织入,通过修改字节码实现,支持字段、构造器等更细粒度的增强,性能更好但使用更复杂。

5. MyBatis 的一级缓存和二级缓存会带来什么问题?

答:一级缓存是 SqlSession 级别的,在同一个 SqlSession 中多次查询同一数据会返回缓存结果,可能导致读到脏数据(另一个 SqlSession 已修改)。二级缓存是 Mapper 级别的,多个 SqlSession 共享,在分布式环境下容易出现数据不一致。建议在分布式环境中关闭二级缓存,使用 Redis 等集中式缓存替代。