SSM框架原理必考题

学习目标

  • 深入理解Spring IOC容器的工作原理和Bean生命周期
  • 掌握Spring AOP的实现原理和应用场景
  • 理解Spring事务管理的原理和传播行为
  • 掌握SpringMVC的请求处理流程
  • 理解MyBatis的工作原理和缓存机制
  • 能够在面试中清晰准确地回答SSM框架相关问题

一、Spring IOC容器原理(15-20题)

1.1 什么是IOC(控制反转)?

标准答案:

IOC(Inversion of Control,控制反转)是一种设计思想,将对象的创建、依赖关系的管理从程序代码中剥离出来,交给IOC容器来管理。

核心要点:

  1. 控制反转:对象的创建控制权从程序代码转移到容器
  2. 依赖注入:容器负责将依赖对象注入到目标对象中
  3. 解耦合:降低组件之间的耦合度,提高代码的可维护性
  4. 配置化:通过配置文件或注解来管理对象

代码示例:

// 传统方式:程序主动创建对象
public class UserService {
    // 程序主动创建依赖对象
    private UserDao userDao = new UserDaoImpl();
    
    public User getUser(Long id) {
        return userDao.findById(id);
    }
}

// IOC方式:容器注入依赖对象
@Service
public class UserService {
    // 容器负责注入依赖对象
    @Autowired
    private UserDao userDao;
    
    public User getUser(Long id) {
        return userDao.findById(id);
    }
}

回答技巧:

  • 先说定义,再说好处
  • 举例说明传统方式和IOC方式的区别
  • 强调解耦合和可维护性的优势

可能的追问:

  • IOC和DI的区别是什么?
  • Spring IOC容器的实现原理是什么?

1.2 什么是DI(依赖注入)?

标准答案:

DI(Dependency Injection,依赖注入)是IOC的一种实现方式,指容器在运行期间,动态地将依赖对象注入到目标对象中。

核心要点:

  1. 注入方式:构造器注入、Setter注入、字段注入
  2. 注入时机:容器启动时或首次使用时
  3. 注入对象:由容器管理的Bean对象
  4. 自动装配:通过@Autowired等注解自动注入

三种注入方式对比:

// 1. 构造器注入(推荐)
@Service
public class UserService {
    private final UserDao userDao;
    
    // 构造器注入,保证依赖不可变
    @Autowired
    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }
}

// 2. Setter注入
@Service
public class UserService {
    private UserDao userDao;
    
    // Setter注入,依赖可选
    @Autowired
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
}

// 3. 字段注入(不推荐)
@Service
public class UserService {
    // 字段注入,简洁但不利于测试
    @Autowired
    private UserDao userDao;
}

回答技巧:

  • 说明DI是IOC的实现方式
  • 列举三种注入方式并说明优缺点
  • 推荐使用构造器注入

可能的追问:

  • 为什么推荐构造器注入?
  • @Autowired和@Resource的区别?

1.3 Spring IOC容器的启动流程是什么?

标准答案:

Spring IOC容器的启动流程主要包括以下几个阶段:

核心要点:

  1. 加载配置:读取XML配置文件或扫描注解配置
  2. 解析BeanDefinition:将配置信息解析为BeanDefinition对象
  3. 注册BeanDefinition:将BeanDefinition注册到BeanFactory中
  4. 实例化Bean:根据BeanDefinition创建Bean实例
  5. 依赖注入:为Bean注入依赖对象
  6. 初始化Bean:调用初始化方法
  7. 容器就绪:容器启动完成,可以使用Bean

流程图:

┌─────────────────────────────────────────────────────────────┐
│              Spring IOC容器启动流程                          │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  1. 加载配置文件/扫描注解                                     │
│           ↓                                                  │
│  2. 解析配置,生成BeanDefinition                             │
│           ↓                                                  │
│  3. 注册BeanDefinition到BeanFactory                          │
│           ↓                                                  │
│  4. 调用BeanFactoryPostProcessor                             │
│           ↓                                                  │
│  5. 实例化Bean(反射创建对象)                                │
│           ↓                                                  │
│  6. 属性赋值(依赖注入)                                      │
│           ↓                                                  │
│  7. 调用Aware接口方法                                         │
│           ↓                                                  │
│  8. 调用BeanPostProcessor.postProcessBeforeInitialization    │
│           ↓                                                  │
│  9. 调用初始化方法(@PostConstruct、InitializingBean)       │
│           ↓                                                  │
│  10. 调用BeanPostProcessor.postProcessAfterInitialization    │
│           ↓                                                  │
│  11. Bean就绪,可以使用                                       │
│           ↓                                                  │
│  12. 容器关闭时调用销毁方法                                   │
│                                                              │
└─────────────────────────────────────────────────────────────┘

代码示例:

// ApplicationContext启动示例
public class SpringIOCDemo {
    public static void main(String[] args) {
        // 1. 创建IOC容器
        ApplicationContext context = 
            new ClassPathXmlApplicationContext("applicationContext.xml");
        
        // 2. 从容器中获取Bean
        UserService userService = context.getBean(UserService.class);
        
        // 3. 使用Bean
        User user = userService.getUser(1L);
        System.out.println(user);
    }
}

回答技巧:

  • 按照流程顺序逐步说明
  • 重点说明BeanDefinition、实例化、依赖注入、初始化几个关键步骤
  • 可以画图辅助说明

可能的追问:

  • BeanDefinition是什么?
  • BeanFactoryPostProcessor和BeanPostProcessor的区别?
  • Bean的作用域有哪些?

1.4 Bean的生命周期是什么?

标准答案:

Bean的生命周期是指Bean从创建到销毁的整个过程,主要包括以下阶段:

核心要点:

  1. 实例化:通过反射创建Bean实例
  2. 属性赋值:注入依赖对象
  3. Aware接口回调:注入容器相关对象
  4. 初始化前处理:BeanPostProcessor前置处理
  5. 初始化:调用初始化方法
  6. 初始化后处理:BeanPostProcessor后置处理
  7. 使用:Bean可以正常使用
  8. 销毁:容器关闭时调用销毁方法

完整的生命周期代码示例:

@Component
public class LifecycleBean implements BeanNameAware, BeanFactoryAware, 
        ApplicationContextAware, InitializingBean, DisposableBean {
    
    private String name;
    
    // 1. 构造器(实例化)
    public LifecycleBean() {
        System.out.println("1. 构造器:创建Bean实例");
    }
    
    // 2. 属性赋值
    @Autowired
    public void setName(String name) {
        this.name = name;
        System.out.println("2. 属性赋值:注入依赖对象");
    }
    
    // 3. Aware接口回调
    @Override
    public void setBeanName(String name) {
        System.out.println("3. BeanNameAware:注入Bean名称 = " + name);
    }
    
    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
        System.out.println("4. BeanFactoryAware:注入BeanFactory");
    }
    
    @Override
    public void setApplicationContext(ApplicationContext context) {
        System.out.println("5. ApplicationContextAware:注入ApplicationContext");
    }
    
    // 4. 初始化前处理(BeanPostProcessor)
    // 由容器自动调用,这里省略
    
    // 5. 初始化方法
    @PostConstruct
    public void postConstruct() {
        System.out.println("6. @PostConstruct:初始化方法1");
    }
    
    @Override
    public void afterPropertiesSet() {
        System.out.println("7. InitializingBean:初始化方法2");
    }
    
    @Bean(initMethod = "init")
    public void init() {
        System.out.println("8. initMethod:初始化方法3");
    }
    
    // 6. 初始化后处理(BeanPostProcessor)
    // 由容器自动调用,这里省略
    
    // 7. Bean可以使用
    public void doSomething() {
        System.out.println("9. Bean正常使用中...");
    }
    
    // 8. 销毁方法
    @PreDestroy
    public void preDestroy() {
        System.out.println("10. @PreDestroy:销毁方法1");
    }
    
    @Override
    public void destroy() {
        System.out.println("11. DisposableBean:销毁方法2");
    }
    
    @Bean(destroyMethod = "destroyMethod")
    public void destroyMethod() {
        System.out.println("12. destroyMethod:销毁方法3");
    }
}

自定义BeanPostProcessor示例:

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        System.out.println("BeanPostProcessor前置处理:" + beanName);
        return bean;
    }
    
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        System.out.println("BeanPostProcessor后置处理:" + beanName);
        return bean;
    }
}

生命周期流程图:

┌──────────────────────────────────────────────────────────────┐
│                    Bean生命周期详细流程                        │
├──────────────────────────────────────────────────────────────┤
│                                                               │
│  1. 实例化Bean(反射调用构造器)                               │
│           ↓                                                   │
│  2. 属性赋值(依赖注入)                                       │
│           ↓                                                   │
│  3. 调用BeanNameAware.setBeanName()                           │
│           ↓                                                   │
│  4. 调用BeanFactoryAware.setBeanFactory()                     │
│           ↓                                                   │
│  5. 调用ApplicationContextAware.setApplicationContext()       │
│           ↓                                                   │
│  6. 调用BeanPostProcessor.postProcessBeforeInitialization()   │
│           ↓                                                   │
│  7. 调用@PostConstruct标注的方法                               │
│           ↓                                                   │
│  8. 调用InitializingBean.afterPropertiesSet()                 │
│           ↓                                                   │
│  9. 调用自定义的init-method方法                                │
│           ↓                                                   │
│  10. 调用BeanPostProcessor.postProcessAfterInitialization()   │
│           ↓                                                   │
│  11. Bean就绪,可以使用                                        │
│           ↓                                                   │
│  12. 容器关闭,调用@PreDestroy标注的方法                       │
│           ↓                                                   │
│  13. 调用DisposableBean.destroy()                             │
│           ↓                                                   │
│  14. 调用自定义的destroy-method方法                            │
│                                                               │
└──────────────────────────────────────────────────────────────┘

回答技巧:

  • 按照时间顺序说明各个阶段
  • 重点说明初始化和销毁两个阶段
  • 可以说明三种初始化方法的执行顺序

可能的追问:

  • 三种初始化方法的执行顺序是什么?
  • BeanPostProcessor的作用是什么?
  • 如何自定义Bean的初始化和销毁方法?

1.5 Bean的作用域有哪些?

标准答案:

Spring支持6种Bean作用域,常用的有5种:

核心要点:

  1. singleton(单例):默认作用域,容器中只有一个Bean实例
  2. prototype(原型):每次获取Bean都创建新实例
  3. request:每个HTTP请求创建一个Bean实例(Web环境)
  4. session:每个HTTP Session创建一个Bean实例(Web环境)
  5. application:整个ServletContext生命周期内只有一个Bean实例
  6. websocket:每个WebSocket连接创建一个Bean实例

作用域对比表:

作用域 说明 使用场景 线程安全
singleton 单例,容器中只有一个实例 无状态Bean(Service、Dao) 需要考虑
prototype 原型,每次获取创建新实例 有状态Bean(Action) 天然线程安全
request 每个HTTP请求一个实例 请求相关数据 线程安全
session 每个HTTP Session一个实例 用户会话数据 线程安全
application 整个应用一个实例 全局共享数据 需要考虑

代码示例:

// 1. singleton作用域(默认)
@Service
@Scope("singleton")  // 可以省略,默认就是singleton
public class UserService {
    // 容器中只有一个UserService实例
}

// 2. prototype作用域
@Component
@Scope("prototype")
public class UserAction {
    // 每次getBean都创建新实例
    private String username;  // 可以有状态
}

// 3. request作用域
@Component
@Scope(value = WebApplicationContext.SCOPE_REQUEST, 
       proxyMode = ScopedProxyMode.TARGET_CLASS)
public class LoginUser {
    // 每个HTTP请求创建一个实例
    private String username;
    private String ip;
}

// 4. session作用域
@Component
@Scope(value = WebApplicationContext.SCOPE_SESSION,
       proxyMode = ScopedProxyMode.TARGET_CLASS)
public class ShoppingCart {
    // 每个HTTP Session创建一个实例
    private List<CartItem> items = new ArrayList<>();
}

singleton和prototype的区别:

@SpringBootTest
public class ScopeTest {
    
    @Autowired
    private ApplicationContext context;
    
    @Test
    public void testSingleton() {
        // singleton:多次获取是同一个实例
        UserService service1 = context.getBean(UserService.class);
        UserService service2 = context.getBean(UserService.class);
        System.out.println(service1 == service2);  // true
    }
    
    @Test
    public void testPrototype() {
        // prototype:多次获取是不同实例
        UserAction action1 = context.getBean(UserAction.class);
        UserAction action2 = context.getBean(UserAction.class);
        System.out.println(action1 == action2);  // false
    }
}

回答技巧:

  • 先说常用的singleton和prototype
  • 再说Web环境下的request和session
  • 说明各自的使用场景

可能的追问:

  • singleton是线程安全的吗?
  • 如何在singleton Bean中注入prototype Bean?
  • 什么时候使用prototype作用域?

1.6 @Autowired和@Resource的区别?

标准答案:

@Autowired和@Resource都用于依赖注入,但它们有以下区别:

核心要点:

  1. 来源不同:@Autowired是Spring提供的,@Resource是JDK提供的(JSR-250)
  2. 装配方式不同:@Autowired按类型装配,@Resource按名称装配
  3. 属性不同:@Autowired有required属性,@Resource有name和type属性
  4. 使用位置不同:@Autowired可用于构造器、字段、方法,@Resource只能用于字段和方法

详细对比表:

维度 @Autowired @Resource
来源 Spring框架 JDK标准(JSR-250)
装配方式 先按类型,再按名称 先按名称,再按类型
必需属性 required(默认true)
指定名称 配合@Qualifier使用 name属性
使用位置 构造器、字段、方法 字段、方法
推荐程度 推荐(Spring生态) 一般

代码示例:

@Service
public class UserService {
    
    // 1. @Autowired按类型装配
    @Autowired
    private UserDao userDao;  // 按UserDao类型查找Bean
    
    // 2. @Autowired + @Qualifier按名称装配
    @Autowired
    @Qualifier("userDaoImpl")
    private UserDao userDao2;  // 按名称"userDaoImpl"查找Bean
    
    // 3. @Autowired的required属性
    @Autowired(required = false)
    private EmailService emailService;  // 找不到Bean不报错
    
    // 4. @Resource按名称装配
    @Resource(name = "userDaoImpl")
    private UserDao userDao3;  // 按名称"userDaoImpl"查找Bean
    
    // 5. @Resource按类型装配
    @Resource
    private UserDao userDao4;  // 按UserDao类型查找Bean
}

装配流程对比:

@Autowired装配流程:
┌─────────────────────────────────────────────────────────┐
│ 1. 按类型查找Bean                                        │
│    ↓                                                     │
│ 2. 找到唯一Bean → 注入成功                               │
│    ↓                                                     │
│ 3. 找到多个Bean → 按字段名称匹配                         │
│    ↓                                                     │
│ 4. 匹配成功 → 注入成功                                   │
│    ↓                                                     │
│ 5. 匹配失败 → 配合@Qualifier指定名称                     │
│    ↓                                                     │
│ 6. 找不到Bean → 抛出异常(required=true)                │
└─────────────────────────────────────────────────────────┘

@Resource装配流程:
┌─────────────────────────────────────────────────────────┐
│ 1. 指定了name属性 → 按名称查找Bean                       │
│    ↓                                                     │
│ 2. 找到Bean → 注入成功                                   │
│    ↓                                                     │
│ 3. 找不到Bean → 抛出异常                                 │
│    ↓                                                     │
│ 4. 未指定name → 按字段名称查找Bean                       │
│    ↓                                                     │
│ 5. 找到Bean → 注入成功                                   │
│    ↓                                                     │
│ 6. 找不到Bean → 按类型查找Bean                           │
│    ↓                                                     │
│ 7. 找到唯一Bean → 注入成功                               │
│    ↓                                                     │
│ 8. 找不到或找到多个 → 抛出异常                           │
└─────────────────────────────────────────────────────────┘

实际使用建议:

// 推荐:使用@Autowired(Spring生态标准)
@Service
public class UserService {
    
    // 推荐:构造器注入
    private final UserDao userDao;
    
    @Autowired
    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }
    
    // 多个实现时,使用@Qualifier指定
    @Autowired
    @Qualifier("mysqlUserDao")
    private UserDao mysqlUserDao;
}

// 不推荐:使用@Resource(除非需要JDK标准)
@Service
public class OrderService {
    @Resource(name = "orderDaoImpl")
    private OrderDao orderDao;
}

回答技巧:

  • 先说来源和装配方式的区别
  • 举例说明两者的使用方式
  • 说明推荐使用@Autowired

可能的追问:

  • 如果有多个相同类型的Bean怎么办?
  • @Autowired是如何实现的?
  • 为什么推荐使用构造器注入?

1.7 Spring如何解决循环依赖?

标准答案:

Spring通过三级缓存机制解决单例Bean的循环依赖问题。

核心要点:

  1. 三级缓存:singletonObjects、earlySingletonObjects、singletonFactories
  2. 提前暴露:Bean实例化后立即放入三级缓存
  3. 延迟注入:先创建实例,再注入依赖
  4. 限制条件:只能解决单例Bean的setter注入循环依赖

三级缓存说明:

// Spring容器中的三级缓存
public class DefaultSingletonBeanRegistry {
    
    // 一级缓存:存放完全初始化好的Bean(成品)
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    
    // 二级缓存:存放提前暴露的Bean(半成品)
    private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
    
    // 三级缓存:存放Bean工厂对象
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
}

循环依赖解决流程:

场景:A依赖B,B依赖A

┌─────────────────────────────────────────────────────────────┐
│              Spring解决循环依赖流程                          │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  1. 创建A的实例(调用构造器)                                 │
│     A = new A();  // 此时A还未注入依赖                       │
│           ↓                                                  │
│  2. 将A的ObjectFactory放入三级缓存                           │
│     singletonFactories.put("a", () -> getEarlyBeanReference(A));│
│           ↓                                                  │
│  3. 为A注入依赖,发现需要B                                   │
│           ↓                                                  │
│  4. 创建B的实例(调用构造器)                                 │
│     B = new B();  // 此时B还未注入依赖                       │
│           ↓                                                  │
│  5. 将B的ObjectFactory放入三级缓存                           │
│     singletonFactories.put("b", () -> getEarlyBeanReference(B));│
│           ↓                                                  │
│  6. 为B注入依赖,发现需要A                                   │
│           ↓                                                  │
│  7. 从三级缓存获取A的ObjectFactory                           │
│     ObjectFactory<?> factory = singletonFactories.get("a"); │
│           ↓                                                  │
│  8. 调用ObjectFactory获取A的早期引用                         │
│     A earlyA = factory.getObject();                         │
│           ↓                                                  │
│  9. 将A的早期引用放入二级缓存                                 │
│     earlySingletonObjects.put("a", earlyA);                 │
│     singletonFactories.remove("a");                         │
│           ↓                                                  │
│  10. 将A注入到B中                                            │
│      B.setA(earlyA);                                        │
│           ↓                                                  │
│  11. B初始化完成,放入一级缓存                                │
│      singletonObjects.put("b", B);                          │
│           ↓                                                  │
│  12. 将B注入到A中                                            │
│      A.setB(B);                                             │
│           ↓                                                  │
│  13. A初始化完成,放入一级缓存                                │
│      singletonObjects.put("a", A);                          │
│      earlySingletonObjects.remove("a");                     │
│           ↓                                                  │
│  14. 循环依赖解决完成                                         │
│                                                              │
└─────────────────────────────────────────────────────────────┘

代码示例:

// 循环依赖示例
@Service
public class ServiceA {
    @Autowired
    private ServiceB serviceB;  // A依赖B
    
    public void doSomething() {
        System.out.println("ServiceA doing something");
        serviceB.doSomething();
    }
}

@Service
public class ServiceB {
    @Autowired
    private ServiceA serviceA;  // B依赖A
    
    public void doSomething() {
        System.out.println("ServiceB doing something");
        serviceA.doSomething();
    }
}

// Spring可以成功创建这两个Bean,因为使用的是setter注入

无法解决的循环依赖场景:

// 1. 构造器注入的循环依赖(无法解决)
@Service
public class ServiceA {
    private ServiceB serviceB;
    
    @Autowired
    public ServiceA(ServiceB serviceB) {  // 构造器注入
        this.serviceB = serviceB;
    }
}

@Service
public class ServiceB {
    private ServiceA serviceA;
    
    @Autowired
    public ServiceB(ServiceA serviceA) {  // 构造器注入
        this.serviceA = serviceA;
    }
}
// 报错:BeanCurrentlyInCreationException

// 2. prototype作用域的循环依赖(无法解决)
@Service
@Scope("prototype")
public class ServiceA {
    @Autowired
    private ServiceB serviceB;
}

@Service
@Scope("prototype")
public class ServiceB {
    @Autowired
    private ServiceA serviceA;
}
// 报错:BeanCurrentlyInCreationException

解决方案:

// 方案1:使用@Lazy延迟注入
@Service
public class ServiceA {
    @Autowired
    @Lazy  // 延迟注入,注入的是代理对象
    private ServiceB serviceB;
}

// 方案2:使用setter注入代替构造器注入
@Service
public class ServiceA {
    private ServiceB serviceB;
    
    @Autowired
    public void setServiceB(ServiceB serviceB) {
        this.serviceB = serviceB;
    }
}

// 方案3:重构代码,消除循环依赖(推荐)
// 将公共逻辑提取到第三个类中
@Service
public class CommonService {
    public void doCommon() {
        // 公共逻辑
    }
}

@Service
public class ServiceA {
    @Autowired
    private CommonService commonService;
}

@Service
public class ServiceB {
    @Autowired
    private CommonService commonService;
}

回答技巧:

  • 先说三级缓存机制
  • 画图说明解决流程
  • 说明哪些情况无法解决
  • 给出解决方案

可能的追问:

  • 为什么需要三级缓存,二级不行吗?
  • 构造器注入为什么无法解决循环依赖?
  • 如何避免循环依赖?

二、Spring AOP原理(10-15题)

2.1 什么是AOP?

标准答案:

AOP(Aspect Oriented Programming,面向切面编程)是一种编程思想,通过预编译方式和运行期动态代理实现程序功能的统一维护。

核心要点:

  1. 横切关注点:分散在各个模块中的相同功能(日志、事务、权限)
  2. 切面:横切关注点的模块化
  3. 织入:将切面应用到目标对象的过程
  4. 解耦:将业务逻辑和系统服务分离

AOP核心概念:

┌─────────────────────────────────────────────────────────────┐
│                    AOP核心概念                               │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  1. Aspect(切面)                                           │
│     - 横切关注点的模块化                                      │
│     - 由切点和通知组成                                        │
│                                                              │
│  2. Join Point(连接点)                                     │
│     - 程序执行的某个特定位置                                  │
│     - 如方法调用、方法执行、异常抛出等                        │
│                                                              │
│  3. Pointcut(切点)                                         │
│     - 匹配连接点的表达式                                      │
│     - 定义在哪些连接点上应用通知                              │
│                                                              │
│  4. Advice(通知)                                           │
│     - 在切点上执行的动作                                      │
│     - 类型:Before、After、AfterReturning、AfterThrowing、Around│
│                                                              │
│  5. Target Object(目标对象)                                │
│     - 被一个或多个切面通知的对象                              │
│                                                              │
│  6. AOP Proxy(AOP代理)                                     │
│     - AOP框架创建的对象,用于实现切面契约                     │
│     - JDK动态代理或CGLIB代理                                 │
│                                                              │
│  7. Weaving(织入)                                          │
│     - 将切面应用到目标对象的过程                              │
│     - 时机:编译期、类加载期、运行期                          │
│                                                              │
└─────────────────────────────────────────────────────────────┘

代码示例:

// 定义切面
@Aspect
@Component
public class LogAspect {
    
    // 定义切点:匹配com.example.service包下所有类的所有方法
    @Pointcut("execution(* com.example.service.*.*(..))")
    public void serviceLayer() {}
    
    // 前置通知:方法执行前执行
    @Before("serviceLayer()")
    public void logBefore(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("方法执行前:" + methodName);
    }
    
    // 后置通知:方法执行后执行(无论是否异常)
    @After("serviceLayer()")
    public void logAfter(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("方法执行后:" + methodName);
    }
    
    // 返回通知:方法正常返回后执行
    @AfterReturning(pointcut = "serviceLayer()", returning = "result")
    public void logAfterReturning(JoinPoint joinPoint, Object result) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("方法返回值:" + result);
    }
    
    // 异常通知:方法抛出异常后执行
    @AfterThrowing(pointcut = "serviceLayer()", throwing = "ex")
    public void logAfterThrowing(JoinPoint joinPoint, Exception ex) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("方法抛出异常:" + ex.getMessage());
    }
    
    // 环绕通知:方法执行前后都执行
    @Around("serviceLayer()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("环绕通知-方法执行前:" + methodName);
        
        long startTime = System.currentTimeMillis();
        Object result = joinPoint.proceed();  // 执行目标方法
        long endTime = System.currentTimeMillis();
        
        System.out.println("环绕通知-方法执行后:" + methodName);
        System.out.println("方法执行耗时:" + (endTime - startTime) + "ms");
        
        return result;
    }
}

AOP应用场景:

// 1. 日志记录
@Aspect
@Component
public class LogAspect {
    @Around("@annotation(com.example.annotation.Log)")
    public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
        // 记录方法调用日志
        log.info("方法调用:{}", joinPoint.getSignature());
        return joinPoint.proceed();
    }
}

// 2. 性能监控
@Aspect
@Component
public class PerformanceAspect {
    @Around("execution(* com.example.service.*.*(..))")
    public Object monitor(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long end = System.currentTimeMillis();
        log.info("方法耗时:{}ms", end - start);
        return result;
    }
}

// 3. 权限校验
@Aspect
@Component
public class AuthAspect {
    @Before("@annotation(com.example.annotation.RequireAuth)")
    public void checkAuth(JoinPoint joinPoint) {
        // 校验用户权限
        if (!hasPermission()) {
            throw new UnauthorizedException("无权限访问");
        }
    }
}

// 4. 事务管理
@Aspect
@Component
public class TransactionAspect {
    @Around("@annotation(org.springframework.transaction.annotation.Transactional)")
    public Object transaction(ProceedingJoinPoint joinPoint) throws Throwable {
        // 开启事务
        beginTransaction();
        try {
            Object result = joinPoint.proceed();
            // 提交事务
            commit();
            return result;
        } catch (Exception e) {
            // 回滚事务
            rollback();
            throw e;
        }
    }
}

回答技巧:

  • 先说定义和作用
  • 列举核心概念
  • 举例说明应用场景
  • 强调解耦的优势

可能的追问:

  • AOP的实现原理是什么?
  • JDK动态代理和CGLIB代理的区别?
  • 五种通知类型的执行顺序是什么?

2.2 Spring AOP的实现原理是什么?

标准答案:

Spring AOP基于动态代理实现,主要有两种代理方式:JDK动态代理和CGLIB代理。

核心要点:

  1. JDK动态代理:基于接口,使用反射机制
  2. CGLIB代理:基于继承,使用字节码技术
  3. 自动选择:有接口用JDK代理,无接口用CGLIB代理
  4. 代理对象:Spring容器返回的是代理对象,而非目标对象

两种代理方式对比:

维度 JDK动态代理 CGLIB代理
实现方式 基于接口 基于继承
要求 目标类必须实现接口 目标类不能是final
性能 创建快,调用慢 创建慢,调用快
使用场景 有接口的类 无接口的类
底层技术 Java反射 ASM字节码

JDK动态代理示例:

// 1. 定义接口
public interface UserService {
    void addUser(String username);
}

// 2. 实现类
public class UserServiceImpl implements UserService {
    @Override
    public void addUser(String username) {
        System.out.println("添加用户:" + username);
    }
}

// 3. 创建代理类
public class JdkProxyFactory {
    
    public static Object createProxy(Object target) {
        return Proxy.newProxyInstance(
            target.getClass().getClassLoader(),  // 类加载器
            target.getClass().getInterfaces(),   // 接口数组
            new InvocationHandler() {            // 调用处理器
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) 
                        throws Throwable {
                    System.out.println("方法执行前");
                    Object result = method.invoke(target, args);  // 调用目标方法
                    System.out.println("方法执行后");
                    return result;
                }
            }
        );
    }
}

// 4. 使用代理
public class JdkProxyTest {
    public static void main(String[] args) {
        UserService target = new UserServiceImpl();
        UserService proxy = (UserService) JdkProxyFactory.createProxy(target);
        proxy.addUser("张三");  // 调用代理方法
    }
}

CGLIB代理示例:

// 1. 目标类(无需实现接口)
public class UserService {
    public void addUser(String username) {
        System.out.println("添加用户:" + username);
    }
}

// 2. 创建代理类
public class CglibProxyFactory {
    
    public static Object createProxy(Object target) {
        // 创建Enhancer对象
        Enhancer enhancer = new Enhancer();
        // 设置父类
        enhancer.setSuperclass(target.getClass());
        // 设置回调函数
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object obj, Method method, Object[] args, 
                    MethodProxy proxy) throws Throwable {
                System.out.println("方法执行前");
                Object result = proxy.invokeSuper(obj, args);  // 调用父类方法
                System.out.println("方法执行后");
                return result;
            }
        });
        // 创建代理对象
        return enhancer.create();
    }
}

// 3. 使用代理
public class CglibProxyTest {
    public static void main(String[] args) {
        UserService target = new UserService();
        UserService proxy = (UserService) CglibProxyFactory.createProxy(target);
        proxy.addUser("张三");  // 调用代理方法
    }
}

Spring AOP代理选择流程:

┌─────────────────────────────────────────────────────────────┐
│              Spring AOP代理选择流程                          │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  1. 检查目标类是否实现了接口                                  │
│           ↓                                                  │
│  2. 实现了接口?                                             │
│     ├─ 是 → 使用JDK动态代理                                  │
│     │        创建接口的代理对象                               │
│     │                                                        │
│     └─ 否 → 使用CGLIB代理                                    │
│              创建子类的代理对象                               │
│                                                              │
│  3. 可以通过配置强制使用CGLIB                                 │
│     @EnableAspectJAutoProxy(proxyTargetClass = true)        │
│                                                              │
└─────────────────────────────────────────────────────────────┘

强制使用CGLIB代理:

// 方式1:注解配置
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)  // 强制使用CGLIB
public class AppConfig {
}

// 方式2:XML配置
<aop:aspectj-autoproxy proxy-target-class="true"/>

代理对象的特点:

@Service
public class UserService {
    public void addUser(String username) {
        System.out.println("添加用户:" + username);
    }
}

@SpringBootTest
public class ProxyTest {
    
    @Autowired
    private UserService userService;
    
    @Test
    public void testProxy() {
        // 打印对象类型
        System.out.println(userService.getClass());
        // 输出:class com.example.service.UserService$$EnhancerBySpringCGLIB$$xxx
        // 说明注入的是CGLIB代理对象
        
        // 判断是否是代理对象
        boolean isProxy = AopUtils.isAopProxy(userService);
        System.out.println("是否是代理对象:" + isProxy);  // true
        
        // 判断是否是CGLIB代理
        boolean isCglibProxy = AopUtils.isCglibProxy(userService);
        System.out.println("是否是CGLIB代理:" + isCglibProxy);  // true
        
        // 判断是否是JDK代理
        boolean isJdkProxy = AopUtils.isJdkDynamicProxy(userService);
        System.out.println("是否是JDK代理:" + isJdkProxy);  // false
    }
}

回答技巧:

  • 先说两种代理方式
  • 对比两种方式的区别
  • 说明Spring如何选择代理方式
  • 举例说明代理的创建过程

可能的追问:

  • 为什么JDK代理必须基于接口?
  • CGLIB代理为什么不能代理final类?
  • 如何获取代理对象的目标对象?

2.3 五种通知类型的执行顺序是什么?

标准答案:

Spring AOP有五种通知类型,它们的执行顺序如下:

核心要点:

  1. @Before:前置通知,方法执行前执行
  2. @Around(前半部分):环绕通知的前半部分
  3. 目标方法执行
  4. @Around(后半部分):环绕通知的后半部分
  5. @After:后置通知,方法执行后执行(无论是否异常)
  6. @AfterReturning:返回通知,方法正常返回后执行
  7. @AfterThrowing:异常通知,方法抛出异常后执行

正常执行流程:

┌─────────────────────────────────────────────────────────────┐
│              通知执行顺序(正常情况)                         │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  1. @Around - 前半部分                                       │
│           ↓                                                  │
│  2. @Before - 前置通知                                       │
│           ↓                                                  │
│  3. 目标方法执行                                             │
│           ↓                                                  │
│  4. @Around - 后半部分                                       │
│           ↓                                                  │
│  5. @After - 后置通知                                        │
│           ↓                                                  │
│  6. @AfterReturning - 返回通知                               │
│                                                              │
└─────────────────────────────────────────────────────────────┘

异常执行流程:

┌─────────────────────────────────────────────────────────────┐
│              通知执行顺序(异常情况)                         │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  1. @Around - 前半部分                                       │
│           ↓                                                  │
│  2. @Before - 前置通知                                       │
│           ↓                                                  │
│  3. 目标方法执行(抛出异常)                                  │
│           ↓                                                  │
│  4. @After - 后置通知                                        │
│           ↓                                                  │
│  5. @AfterThrowing - 异常通知                                │
│           ↓                                                  │
│  6. @Around - 异常处理(如果有)                             │
│                                                              │
└─────────────────────────────────────────────────────────────┘

完整示例代码:

@Aspect
@Component
public class OrderAspect {
    
    // 环绕通知
    @Around("execution(* com.example.service.OrderService.createOrder(..))")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("1. @Around - 前半部分");
        try {
            Object result = joinPoint.proceed();  // 执行目标方法
            System.out.println("4. @Around - 后半部分(正常返回)");
            return result;
        } catch (Exception e) {
            System.out.println("4. @Around - 后半部分(异常处理)");
            throw e;
        }
    }
    
    // 前置通知
    @Before("execution(* com.example.service.OrderService.createOrder(..))")
    public void before(JoinPoint joinPoint) {
        System.out.println("2. @Before - 前置通知");
    }
    
    // 后置通知
    @After("execution(* com.example.service.OrderService.createOrder(..))")
    public void after(JoinPoint joinPoint) {
        System.out.println("5. @After - 后置通知");
    }
    
    // 返回通知
    @AfterReturning(pointcut = "execution(* com.example.service.OrderService.createOrder(..))", 
                    returning = "result")
    public void afterReturning(JoinPoint joinPoint, Object result) {
        System.out.println("6. @AfterReturning - 返回通知,返回值:" + result);
    }
    
    // 异常通知
    @AfterThrowing(pointcut = "execution(* com.example.service.OrderService.createOrder(..))", 
                   throwing = "ex")
    public void afterThrowing(JoinPoint joinPoint, Exception ex) {
        System.out.println("6. @AfterThrowing - 异常通知,异常:" + ex.getMessage());
    }
}

// 目标类
@Service
public class OrderService {
    
    public String createOrder(String orderNo) {
        System.out.println("3. 目标方法执行:创建订单 " + orderNo);
        return "订单创建成功";
    }
    
    public String createOrderWithException(String orderNo) {
        System.out.println("3. 目标方法执行:创建订单 " + orderNo);
        throw new RuntimeException("订单创建失败");
    }
}

// 测试类
@SpringBootTest
public class AspectOrderTest {
    
    @Autowired
    private OrderService orderService;
    
    @Test
    public void testNormalFlow() {
        System.out.println("===== 正常执行流程 =====");
        orderService.createOrder("ORDER001");
        /*
        输出:
        1. @Around - 前半部分
        2. @Before - 前置通知
        3. 目标方法执行:创建订单 ORDER001
        4. @Around - 后半部分(正常返回)
        5. @After - 后置通知
        6. @AfterReturning - 返回通知,返回值:订单创建成功
        */
    }
    
    @Test
    public void testExceptionFlow() {
        System.out.println("===== 异常执行流程 =====");
        try {
            orderService.createOrderWithException("ORDER002");
        } catch (Exception e) {
            // 捕获异常
        }
        /*
        输出:
        1. @Around - 前半部分
        2. @Before - 前置通知
        3. 目标方法执行:创建订单 ORDER002
        5. @After - 后置通知
        6. @AfterThrowing - 异常通知,异常:订单创建失败
        4. @Around - 后半部分(异常处理)
        */
    }
}

多个切面的执行顺序:

// 使用@Order注解指定切面的执行顺序
@Aspect
@Component
@Order(1)  // 数字越小,优先级越高
public class FirstAspect {
    @Before("execution(* com.example.service.*.*(..))")
    public void before() {
        System.out.println("FirstAspect - Before");
    }
}

@Aspect
@Component
@Order(2)
public class SecondAspect {
    @Before("execution(* com.example.service.*.*(..))")
    public void before() {
        System.out.println("SecondAspect - Before");
    }
}

// 执行顺序:
// FirstAspect - Before
// SecondAspect - Before
// 目标方法执行
// SecondAspect - After
// FirstAspect - After

回答技巧:

  • 按照执行顺序逐一说明
  • 区分正常流程和异常流程
  • 举例说明实际执行情况
  • 说明多个切面的执行顺序

可能的追问:

  • @After和@AfterReturning的区别?
  • 如何控制多个切面的执行顺序?
  • 环绕通知中如何处理异常?

三、Spring事务管理(10-15题)

3.1 Spring事务管理的实现方式有哪些?

标准答案:

Spring提供两种事务管理方式:编程式事务和声明式事务。

核心要点:

  1. 编程式事务:通过TransactionTemplate或PlatformTransactionManager手动管理事务
  2. 声明式事务:通过@Transactional注解或XML配置自动管理事务
  3. 推荐方式:声明式事务,代码侵入性小,易于维护
  4. 底层实现:基于AOP实现

两种方式对比:

维度 编程式事务 声明式事务
实现方式 手动编码 注解或XML配置
代码侵入性
灵活性
维护性
使用场景 复杂事务逻辑 常规业务场景
推荐程度 不推荐 推荐

编程式事务示例:

// 方式1:使用TransactionTemplate
@Service
public class OrderService {
    
    @Autowired
    private TransactionTemplate transactionTemplate;
    
    @Autowired
    private OrderDao orderDao;
    
    public void createOrder(Order order) {
        transactionTemplate.execute(status -> {
            try {
                // 业务逻辑
                orderDao.insert(order);
                // 更多操作...
                return null;
            } catch (Exception e) {
                // 回滚事务
                status.setRollbackOnly();
                throw e;
            }
        });
    }
}

// 方式2:使用PlatformTransactionManager
@Service
public class OrderService {
    
    @Autowired
    private PlatformTransactionManager transactionManager;
    
    @Autowired
    private OrderDao orderDao;
    
    public void createOrder(Order order) {
        // 定义事务属性
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        
        // 开启事务
        TransactionStatus status = transactionManager.getTransaction(def);
        
        try {
            // 业务逻辑
            orderDao.insert(order);
            // 更多操作...
            
            // 提交事务
            transactionManager.commit(status);
        } catch (Exception e) {
            // 回滚事务
            transactionManager.rollback(status);
            throw e;
        }
    }
}

声明式事务示例:

// 方式1:使用@Transactional注解(推荐)
@Service
public class OrderService {
    
    @Autowired
    private OrderDao orderDao;
    
    @Autowired
    private ProductDao productDao;
    
    // 在方法上添加@Transactional注解
    @Transactional(rollbackFor = Exception.class)
    public void createOrder(Order order) {
        // 1. 创建订单
        orderDao.insert(order);
        
        // 2. 扣减库存
        productDao.deductStock(order.getProductId(), order.getQuantity());
        
        // 3. 更多操作...
        // 如果任何操作失败,整个事务回滚
    }
}

// 方式2:在类上添加@Transactional注解
@Service
@Transactional(rollbackFor = Exception.class)  // 类上的注解对所有方法生效
public class OrderService {
    
    @Autowired
    private OrderDao orderDao;
    
    public void createOrder(Order order) {
        orderDao.insert(order);
    }
    
    public void updateOrder(Order order) {
        orderDao.update(order);
    }
    
    // 只读事务,提高性能
    @Transactional(readOnly = true)
    public Order getOrder(Long id) {
        return orderDao.selectById(id);
    }
}

@Transactional注解属性:

@Transactional(
    // 事务传播行为
    propagation = Propagation.REQUIRED,
    
    // 事务隔离级别
    isolation = Isolation.DEFAULT,
    
    // 事务超时时间(秒)
    timeout = 30,
    
    // 是否只读事务
    readOnly = false,
    
    // 遇到哪些异常回滚
    rollbackFor = {Exception.class},
    
    // 遇到哪些异常不回滚
    noRollbackFor = {IllegalArgumentException.class}
)
public void createOrder(Order order) {
    // 业务逻辑
}

声明式事务的底层实现:

┌─────────────────────────────────────────────────────────────┐
│            声明式事务的AOP实现原理                            │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  1. Spring扫描@Transactional注解                             │
│           ↓                                                  │
│  2. 为标注的类/方法创建AOP代理                                │
│           ↓                                                  │
│  3. 调用方法时,代理拦截                                      │
│           ↓                                                  │
│  4. 开启事务(TransactionInterceptor)                       │
│     - 获取数据库连接                                          │
│     - 设置autoCommit=false                                   │
│           ↓                                                  │
│  5. 执行目标方法                                             │
│           ↓                                                  │
│  6. 方法正常返回                                             │
│     - 提交事务(commit)                                      │
│     - 释放连接                                               │
│           ↓                                                  │
│  7. 方法抛出异常                                             │
│     - 回滚事务(rollback)                                    │
│     - 释放连接                                               │
│                                                              │
└─────────────────────────────────────────────────────────────┘

回答技巧:

  • 先说两种方式
  • 对比两种方式的优缺点
  • 推荐使用声明式事务
  • 说明底层基于AOP实现

可能的追问:

  • @Transactional注解有哪些属性?
  • 事务的传播行为有哪些?
  • 事务什么时候会失效?

3.2 Spring事务的传播行为有哪些?

标准答案:

Spring定义了7种事务传播行为,用于控制事务方法之间的调用关系。

核心要点:

  1. REQUIRED(默认):如果当前有事务,加入该事务;如果没有,创建新事务
  2. REQUIRES_NEW:总是创建新事务,如果当前有事务,挂起当前事务
  3. SUPPORTS:如果当前有事务,加入该事务;如果没有,以非事务方式执行
  4. NOT_SUPPORTED:以非事务方式执行,如果当前有事务,挂起当前事务
  5. MANDATORY:必须在事务中执行,如果当前没有事务,抛出异常
  6. NEVER:必须在非事务中执行,如果当前有事务,抛出异常
  7. NESTED:如果当前有事务,在嵌套事务中执行;如果没有,创建新事务

传播行为详解表:

传播行为 说明 使用场景
REQUIRED 支持当前事务,没有则创建 最常用,默认值
REQUIRES_NEW 总是创建新事务 独立事务,不受外部影响
SUPPORTS 支持当前事务,没有则非事务 查询方法
NOT_SUPPORTED 以非事务方式执行 不需要事务的操作
MANDATORY 必须在事务中执行 强制事务约束
NEVER 必须在非事务中执行 禁止事务
NESTED 嵌套事务 部分回滚场景

REQUIRED传播行为示例:

@Service
public class OrderService {
    
    @Autowired
    private ProductService productService;
    
    // 方法A:有事务
    @Transactional(propagation = Propagation.REQUIRED)
    public void createOrder(Order order) {
        // 1. 创建订单
        orderDao.insert(order);
        
        // 2. 调用方法B
        productService.deductStock(order.getProductId(), order.getQuantity());
        
        // 方法A和方法B在同一个事务中
        // 如果方法B抛出异常,方法A也会回滚
    }
}

@Service
public class ProductService {
    
    // 方法B:REQUIRED传播行为
    @Transactional(propagation = Propagation.REQUIRED)
    public void deductStock(Long productId, Integer quantity) {
        // 扣减库存
        productDao.deductStock(productId, quantity);
        
        // 如果当前有事务(方法A的事务),加入该事务
        // 如果当前没有事务,创建新事务
    }
}

REQUIRES_NEW传播行为示例:

@Service
public class OrderService {
    
    @Autowired
    private LogService logService;
    
    // 方法A:有事务
    @Transactional(propagation = Propagation.REQUIRED)
    public void createOrder(Order order) {
        // 1. 创建订单
        orderDao.insert(order);
        
        // 2. 记录日志(独立事务)
        logService.saveLog("创建订单:" + order.getOrderNo());
        
        // 3. 模拟异常
        if (order.getAmount() > 10000) {
            throw new RuntimeException("订单金额过大");
        }
        
        // 方法A回滚,但方法B不回滚(独立事务)
        // 日志仍然会保存成功
    }
}

@Service
public class LogService {
    
    // 方法B:REQUIRES_NEW传播行为
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void saveLog(String content) {
        // 保存日志
        logDao.insert(new Log(content));
        
        // 总是创建新事务,不受外部事务影响
        // 即使外部事务回滚,这里的日志也会提交
    }
}

NESTED传播行为示例:

@Service
public class OrderService {
    
    @Autowired
    private CouponService couponService;
    
    // 方法A:有事务
    @Transactional(propagation = Propagation.REQUIRED)
    public void createOrder(Order order) {
        // 1. 创建订单
        orderDao.insert(order);
        
        // 2. 使用优惠券(嵌套事务)
        try {
            couponService.useCoupon(order.getCouponId());
        } catch (Exception e) {
            // 优惠券使用失败,只回滚优惠券操作
            // 订单创建不受影响
            log.warn("优惠券使用失败", e);
        }
        
        // 3. 继续其他操作
    }
}

@Service
public class CouponService {
    
    // 方法B:NESTED传播行为
    @Transactional(propagation = Propagation.NESTED)
    public void useCoupon(Long couponId) {
        // 使用优惠券
        couponDao.updateStatus(couponId, "USED");
        
        // 在嵌套事务中执行
        // 如果失败,只回滚嵌套事务,不影响外部事务
    }
}

传播行为组合场景:

场景1:REQUIRED + REQUIRED
┌─────────────────────────────────────────────────────────────┐
│  方法A(REQUIRED)                                            │
│  ┌───────────────────────────────────────────────────────┐  │
│  │ 开启事务                                               │  │
│  │   ↓                                                    │  │
│  │ 执行方法A的业务逻辑                                     │  │
│  │   ↓                                                    │  │
│  │ 调用方法B(REQUIRED)                                   │  │
│  │   ↓                                                    │  │
│  │ 方法B加入方法A的事务                                    │  │
│  │   ↓                                                    │  │
│  │ 执行方法B的业务逻辑                                     │  │
│  │   ↓                                                    │  │
│  │ 方法B执行完成                                          │  │
│  │   ↓                                                    │  │
│  │ 继续执行方法A的业务逻辑                                 │  │
│  │   ↓                                                    │  │
│  │ 提交事务(或回滚)                                      │  │
│  └───────────────────────────────────────────────────────┘  │
│  结论:方法A和方法B在同一个事务中                           │
└─────────────────────────────────────────────────────────────┘

场景2:REQUIRED + REQUIRES_NEW
┌─────────────────────────────────────────────────────────────┐
│  方法A(REQUIRED)                                            │
│  ┌───────────────────────────────────────────────────────┐  │
│  │ 开启事务A                                              │  │
│  │   ↓                                                    │  │
│  │ 执行方法A的业务逻辑                                     │  │
│  │   ↓                                                    │  │
│  │ 调用方法B(REQUIRES_NEW)                              │  │
│  │   ↓                                                    │  │
│  │ 挂起事务A                                              │  │
│  │   ↓                                                    │  │
│  │ 开启新事务B                                            │  │
│  │   ↓                                                    │  │
│  │ 执行方法B的业务逻辑                                     │  │
│  │   ↓                                                    │  │
│  │ 提交事务B                                              │  │
│  │   ↓                                                    │  │
│  │ 恢复事务A                                              │  │
│  │   ↓                                                    │  │
│  │ 继续执行方法A的业务逻辑                                 │  │
│  │   ↓                                                    │  │
│  │ 提交事务A(或回滚)                                     │  │
│  └───────────────────────────────────────────────────────┘  │
│  结论:方法A和方法B在不同的事务中,互不影响                 │
└─────────────────────────────────────────────────────────────┘

场景3:REQUIRED + NESTED
┌─────────────────────────────────────────────────────────────┐
│  方法A(REQUIRED)                                            │
│  ┌───────────────────────────────────────────────────────┐  │
│  │ 开启事务                                               │  │
│  │   ↓                                                    │  │
│  │ 执行方法A的业务逻辑                                     │  │
│  │   ↓                                                    │  │
│  │ 设置保存点(Savepoint)                                │  │
│  │   ↓                                                    │  │
│  │ 调用方法B(NESTED)                                     │  │
│  │   ↓                                                    │  │
│  │ 执行方法B的业务逻辑                                     │  │
│  │   ↓                                                    │  │
│  │ 方法B异常 → 回滚到保存点                               │  │
│  │ 方法B成功 → 继续执行                                   │  │
│  │   ↓                                                    │  │
│  │ 继续执行方法A的业务逻辑                                 │  │
│  │   ↓                                                    │  │
│  │ 提交事务(或回滚)                                      │  │
│  └───────────────────────────────────────────────────────┘  │
│  结论:方法B在方法A的事务中,但可以部分回滚                 │
└─────────────────────────────────────────────────────────────┘

实际应用场景:

// 场景1:订单创建(REQUIRED)
@Transactional(propagation = Propagation.REQUIRED)
public void createOrder(Order order) {
    // 创建订单、扣减库存、清空购物车
    // 这些操作必须在同一个事务中
}

// 场景2:日志记录(REQUIRES_NEW)
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveLog(String content) {
    // 日志记录独立事务,不受外部事务影响
    // 即使外部事务回滚,日志也要保存
}

// 场景3:查询操作(SUPPORTS)
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public List<Order> listOrders() {
    // 查询操作,如果有事务就加入,没有就非事务执行
}

// 场景4:优惠券使用(NESTED)
@Transactional(propagation = Propagation.NESTED)
public void useCoupon(Long couponId) {
    // 优惠券使用失败不影响订单创建
    // 但订单创建失败会导致优惠券回滚
}

回答技巧:

  • 重点说明REQUIRED、REQUIRES_NEW、NESTED三种
  • 画图说明不同传播行为的事务关系
  • 举例说明实际应用场景
  • 说明默认值是REQUIRED

可能的追问:

  • REQUIRES_NEW和NESTED的区别?
  • 如何选择合适的传播行为?
  • 传播行为失效的场景有哪些?

3.3 Spring事务什么时候会失效?

标准答案:

Spring事务在以下场景会失效:

核心要点:

  1. 方法不是public:@Transactional只能作用于public方法
  2. 方法被final修饰:CGLIB无法代理final方法
  3. 同类方法调用:内部方法调用不经过代理
  4. 异常被捕获:异常被catch后没有抛出
  5. 异常类型不匹配:默认只回滚RuntimeException和Error
  6. 数据库不支持事务:如MyISAM引擎
  7. 没有被Spring管理:类没有@Service等注解

场景1:方法不是public

@Service
public class OrderService {
    
    // ❌ 错误:private方法,事务失效
    @Transactional
    private void createOrder(Order order) {
        orderDao.insert(order);
    }
    
    // ✓ 正确:public方法,事务生效
    @Transactional
    public void createOrderCorrect(Order order) {
        orderDao.insert(order);
    }
}

场景2:同类方法调用

@Service
public class OrderService {
    
    // 方法A:无事务
    public void methodA() {
        // 直接调用方法B,不经过代理
        this.methodB();  // ❌ 事务失效
    }
    
    // 方法B:有事务
    @Transactional
    public void methodB() {
        orderDao.insert(new Order());
    }
}

// 解决方案1:注入自己
@Service
public class OrderService {
    
    @Autowired
    private OrderService self;  // 注入自己
    
    public void methodA() {
        // 通过代理对象调用
        self.methodB();  // ✓ 事务生效
    }
    
    @Transactional
    public void methodB() {
        orderDao.insert(new Order());
    }
}

// 解决方案2:使用AopContext
@Service
public class OrderService {
    
    public void methodA() {
        // 获取代理对象
        OrderService proxy = (OrderService) AopContext.currentProxy();
        proxy.methodB();  // ✓ 事务生效
    }
    
    @Transactional
    public void methodB() {
        orderDao.insert(new Order());
    }
}

// 解决方案3:拆分到不同类(推荐)
@Service
public class OrderService {
    
    @Autowired
    private OrderInnerService orderInnerService;
    
    public void methodA() {
        orderInnerService.methodB();  // ✓ 事务生效
    }
}

@Service
public class OrderInnerService {
    
    @Transactional
    public void methodB() {
        orderDao.insert(new Order());
    }
}

场景3:异常被捕获

@Service
public class OrderService {
    
    // ❌ 错误:异常被捕获,事务不回滚
    @Transactional
    public void createOrder(Order order) {
        try {
            orderDao.insert(order);
            // 模拟异常
            int i = 1 / 0;
        } catch (Exception e) {
            // 异常被捕获,事务不会回滚
            log.error("创建订单失败", e);
        }
    }
    
    // ✓ 正确:捕获后重新抛出
    @Transactional
    public void createOrderCorrect(Order order) {
        try {
            orderDao.insert(order);
            int i = 1 / 0;
        } catch (Exception e) {
            log.error("创建订单失败", e);
            throw e;  // 重新抛出异常
        }
    }
    
    // ✓ 正确:手动回滚
    @Transactional
    public void createOrderManual(Order order) {
        try {
            orderDao.insert(order);
            int i = 1 / 0;
        } catch (Exception e) {
            log.error("创建订单失败", e);
            // 手动标记回滚
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
    }
}

场景4:异常类型不匹配

@Service
public class OrderService {
    
    // ❌ 错误:抛出检查异常,事务不回滚
    @Transactional
    public void createOrder(Order order) throws Exception {
        orderDao.insert(order);
        // 抛出检查异常,默认不回滚
        throw new Exception("创建订单失败");
    }
    
    // ✓ 正确:指定回滚的异常类型
    @Transactional(rollbackFor = Exception.class)
    public void createOrderCorrect(Order order) throws Exception {
        orderDao.insert(order);
        throw new Exception("创建订单失败");  // 会回滚
    }
    
    // ✓ 正确:抛出运行时异常
    @Transactional
    public void createOrderRuntime(Order order) {
        orderDao.insert(order);
        throw new RuntimeException("创建订单失败");  // 会回滚
    }
}

场景5:数据库不支持事务

-- ❌ 错误:MyISAM引擎不支持事务
CREATE TABLE `order` (
  `id` BIGINT NOT NULL AUTO_INCREMENT,
  `order_no` VARCHAR(32) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;

-- ✓ 正确:InnoDB引擎支持事务
CREATE TABLE `order` (
  `id` BIGINT NOT NULL AUTO_INCREMENT,
  `order_no` VARCHAR(32) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

场景6:没有被Spring管理

// ❌ 错误:没有@Service注解,不被Spring管理
public class OrderService {
    
    @Transactional
    public void createOrder(Order order) {
        orderDao.insert(order);
    }
}

// ✓ 正确:添加@Service注解
@Service
public class OrderService {
    
    @Transactional
    public void createOrder(Order order) {
        orderDao.insert(order);
    }
}

事务失效检查清单:

┌─────────────────────────────────────────────────────────────┐
│              Spring事务失效检查清单                          │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  □ 方法是否是public?                                        │
│  □ 方法是否被final修饰?                                     │
│  □ 是否是同类方法调用?                                      │
│  □ 异常是否被捕获?                                          │
│  □ 异常类型是否匹配rollbackFor?                             │
│  □ 数据库引擎是否支持事务?                                  │
│  □ 类是否被Spring管理?                                      │
│  □ 是否开启了事务管理?(@EnableTransactionManagement)      │
│                                                              │
└─────────────────────────────────────────────────────────────┘

回答技巧:

  • 列举常见的失效场景
  • 每个场景给出错误示例和正确示例
  • 说明失效的原因
  • 给出解决方案

可能的追问:

  • 如何排查事务失效问题?
  • 如何验证事务是否生效?
  • 事务的最佳实践有哪些?

四、SpringMVC请求处理流程(10-15题)

4.1 SpringMVC的请求处理流程是什么?

标准答案:

SpringMVC的请求处理流程包括以下步骤:

核心要点:

  1. DispatcherServlet接收请求:前端控制器接收所有请求
  2. HandlerMapping查找Handler:根据URL查找对应的处理器
  3. HandlerAdapter执行Handler:适配器执行处理器方法
  4. ModelAndView返回:处理器返回模型和视图
  5. ViewResolver解析视图:视图解析器解析视图名称
  6. View渲染:视图渲染并返回响应

完整流程图:

┌─────────────────────────────────────────────────────────────┐
│            SpringMVC请求处理完整流程                         │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  1. 用户发送请求                                             │
│           ↓                                                  │
│  2. DispatcherServlet接收请求                                │
│     - 前端控制器,统一处理所有请求                            │
│           ↓                                                  │
│  3. DispatcherServlet调用HandlerMapping                      │
│     - 根据请求URL查找对应的Handler                            │
│     - 返回HandlerExecutionChain(Handler + 拦截器)          │
│           ↓                                                  │
│  4. DispatcherServlet调用HandlerAdapter                      │
│     - 适配器模式,适配不同类型的Handler                       │
│     - 执行拦截器的preHandle方法                               │
│           ↓                                                  │
│  5. HandlerAdapter执行Handler                                │
│     - 调用Controller的方法                                   │
│     - 参数解析、数据绑定、参数校验                            │
│           ↓                                                  │
│  6. Handler返回ModelAndView                                  │
│     - Model:模型数据                                        │
│     - View:视图名称或视图对象                                │
│           ↓                                                  │
│  7. 执行拦截器的postHandle方法                                │
│           ↓                                                  │
│  8. DispatcherServlet调用ViewResolver                        │
│     - 视图解析器解析视图名称                                  │
│     - 返回View对象                                           │
│           ↓                                                  │
│  9. DispatcherServlet调用View.render()                       │
│     - 视图渲染,将模型数据填充到视图                          │
│     - 生成HTML响应                                           │
│           ↓                                                  │
│  10. 执行拦截器的afterCompletion方法                          │
│           ↓                                                  │
│  11. 返回响应给用户                                           │
│                                                              │
└─────────────────────────────────────────────────────────────┘

核心组件说明:

// 1. DispatcherServlet:前端控制器
public class DispatcherServlet extends FrameworkServlet {
    
    @Override
    protected void doDispatch(HttpServletRequest request, 
                             HttpServletResponse response) {
        // 查找Handler
        HandlerExecutionChain mappedHandler = getHandler(request);
        
        // 获取HandlerAdapter
        HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
        
        // 执行Handler
        ModelAndView mv = ha.handle(request, response, mappedHandler.getHandler());
        
        // 渲染视图
        render(mv, request, response);
    }
}

// 2. HandlerMapping:处理器映射器
public interface HandlerMapping {
    // 根据请求查找Handler
    HandlerExecutionChain getHandler(HttpServletRequest request);
}

// 3. HandlerAdapter:处理器适配器
public interface HandlerAdapter {
    // 执行Handler
    ModelAndView handle(HttpServletRequest request, 
                       HttpServletResponse response, 
                       Object handler);
}

// 4. ViewResolver:视图解析器
public interface ViewResolver {
    // 解析视图名称
    View resolveViewName(String viewName, Locale locale);
}

// 5. View:视图
public interface View {
    // 渲染视图
    void render(Map<String, ?> model, 
               HttpServletRequest request, 
               HttpServletResponse response);
}

完整示例代码:

// 1. Controller
@Controller
@RequestMapping("/user")
public class UserController {
    
    @Autowired
    private UserService userService;
    
    // 处理请求
    @GetMapping("/{id}")
    public String getUser(@PathVariable Long id, Model model) {
        // 查询用户
        User user = userService.getUser(id);
        
        // 添加模型数据
        model.addAttribute("user", user);
        
        // 返回视图名称
        return "user/detail";
    }
    
    // 返回JSON数据
    @GetMapping("/json/{id}")
    @ResponseBody
    public User getUserJson(@PathVariable Long id) {
        return userService.getUser(id);
    }
}

// 2. 拦截器
@Component
public class LoginInterceptor implements HandlerInterceptor {
    
    // 前置处理
    @Override
    public boolean preHandle(HttpServletRequest request, 
                            HttpServletResponse response, 
                            Object handler) {
        System.out.println("preHandle:请求处理前");
        // 返回true继续执行,返回false中断请求
        return true;
    }
    
    // 后置处理
    @Override
    public void postHandle(HttpServletRequest request, 
                          HttpServletResponse response, 
                          Object handler, 
                          ModelAndView modelAndView) {
        System.out.println("postHandle:请求处理后");
    }
    
    // 完成处理
    @Override
    public void afterCompletion(HttpServletRequest request, 
                               HttpServletResponse response, 
                               Object handler, 
                               Exception ex) {
        System.out.println("afterCompletion:视图渲染后");
    }
}

// 3. 配置拦截器
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    
    @Autowired
    private LoginInterceptor loginInterceptor;
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/**")  // 拦截所有请求
                .excludePathPatterns("/login", "/register");  // 排除登录注册
    }
}

// 4. 视图解析器配置
@Configuration
public class ViewResolverConfig {
    
    @Bean
    public ViewResolver viewResolver() {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/views/");  // 视图前缀
        resolver.setSuffix(".jsp");  // 视图后缀
        return resolver;
    }
}

请求处理示例:

用户请求:GET /user/1

1. DispatcherServlet接收请求
   - URL: /user/1
   - Method: GET

2. HandlerMapping查找Handler
   - 找到:UserController.getUser()方法
   - 返回:HandlerExecutionChain(包含Handler和拦截器)

3. 执行拦截器的preHandle方法
   - LoginInterceptor.preHandle()
   - 返回true,继续执行

4. HandlerAdapter执行Handler
   - 解析参数:@PathVariable Long id = 1
   - 调用方法:UserController.getUser(1, model)
   - 返回:ModelAndView("user/detail", {user: User对象})

5. 执行拦截器的postHandle方法
   - LoginInterceptor.postHandle()

6. ViewResolver解析视图
   - 视图名称:"user/detail"
   - 解析结果:"/WEB-INF/views/user/detail.jsp"

7. View渲染
   - 将User对象填充到JSP页面
   - 生成HTML响应

8. 执行拦截器的afterCompletion方法
   - LoginInterceptor.afterCompletion()

9. 返回响应给用户
   - 返回渲染后的HTML页面

回答技巧:

  • 按照流程顺序逐步说明
  • 重点说明核心组件的作用
  • 画图辅助说明
  • 举例说明实际处理过程

可能的追问:

  • DispatcherServlet的初始化过程?
  • HandlerMapping如何查找Handler?
  • 拦截器和过滤器的区别?

4.2 SpringMVC的核心组件有哪些?

标准答案:

SpringMVC的核心组件包括:

核心要点:

  1. DispatcherServlet:前端控制器,统一处理请求
  2. HandlerMapping:处理器映射器,查找Handler
  3. HandlerAdapter:处理器适配器,执行Handler
  4. Handler:处理器,即Controller
  5. ViewResolver:视图解析器,解析视图
  6. View:视图,渲染响应
  7. HandlerInterceptor:拦截器,拦截请求

组件详解表:

组件 作用 常用实现类
DispatcherServlet 前端控制器 DispatcherServlet
HandlerMapping 查找Handler RequestMappingHandlerMapping
HandlerAdapter 执行Handler RequestMappingHandlerAdapter
Handler 处理请求 @Controller标注的类
ViewResolver 解析视图 InternalResourceViewResolver
View 渲染视图 JstlView、JsonView
HandlerInterceptor 拦截请求 自定义拦截器

组件关系图:

┌─────────────────────────────────────────────────────────────┐
│              SpringMVC核心组件关系图                         │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│                    DispatcherServlet                         │
│                    (前端控制器)                             │
│                          │                                   │
│         ┌────────────────┼────────────────┐                 │
│         │                │                │                 │
│         ↓                ↓                ↓                 │
│  HandlerMapping   HandlerAdapter   ViewResolver             │
│  (映射器)        (适配器)       (视图解析器)            │
│         │                │                │                 │
│         ↓                ↓                ↓                 │
│     Handler          Handler           View                 │
│   (处理器)        (处理器)        (视图)                │
│         │                │                │                 │
│         └────────────────┼────────────────┘                 │
│                          │                                   │
│                  HandlerInterceptor                          │
│                    (拦截器)                                 │
│                                                              │
└─────────────────────────────────────────────────────────────┘

各组件详细说明:

// 1. DispatcherServlet:前端控制器
// 作用:接收所有请求,协调各组件工作
@WebServlet(urlPatterns = "/", loadOnStartup = 1)
public class DispatcherServlet extends FrameworkServlet {
    
    // 核心方法:处理请求
    protected void doDispatch(HttpServletRequest request, 
                             HttpServletResponse response) {
        // 1. 查找Handler
        HandlerExecutionChain mappedHandler = getHandler(request);
        
        // 2. 获取HandlerAdapter
        HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
        
        // 3. 执行拦截器preHandle
        if (!mappedHandler.applyPreHandle(request, response)) {
            return;
        }
        
        // 4. 执行Handler
        ModelAndView mv = ha.handle(request, response, mappedHandler.getHandler());
        
        // 5. 执行拦截器postHandle
        mappedHandler.applyPostHandle(request, response, mv);
        
        // 6. 渲染视图
        processDispatchResult(request, response, mappedHandler, mv, null);
    }
}

// 2. HandlerMapping:处理器映射器
// 作用:根据URL查找对应的Handler
public interface HandlerMapping {
    
    // 查找Handler
    HandlerExecutionChain getHandler(HttpServletRequest request);
}

// 常用实现:RequestMappingHandlerMapping
// 处理@RequestMapping注解
public class RequestMappingHandlerMapping extends AbstractHandlerMapping {
    
    // 注册Handler
    protected void registerHandlerMethod(Object handler, Method method, 
                                        RequestMappingInfo mapping) {
        // 将@RequestMapping注解的方法注册为Handler
    }
}

// 3. HandlerAdapter:处理器适配器
// 作用:执行Handler,适配不同类型的Handler
public interface HandlerAdapter {
    
    // 是否支持该Handler
    boolean supports(Object handler);
    
    // 执行Handler
    ModelAndView handle(HttpServletRequest request, 
                       HttpServletResponse response, 
                       Object handler);
}

// 常用实现:RequestMappingHandlerAdapter
// 处理@RequestMapping注解的方法
public class RequestMappingHandlerAdapter extends AbstractHandlerAdapter {
    
    // 执行Handler方法
    protected ModelAndView invokeHandlerMethod(HttpServletRequest request, 
                                              HttpServletResponse response, 
                                              HandlerMethod handlerMethod) {
        // 1. 参数解析
        Object[] args = getMethodArgumentValues(request, handlerMethod);
        
        // 2. 调用方法
        Object returnValue = handlerMethod.invoke(args);
        
        // 3. 处理返回值
        return getModelAndView(returnValue);
    }
}

// 4. Handler:处理器
// 作用:处理具体的业务逻辑
@Controller
@RequestMapping("/user")
public class UserController {
    
    @GetMapping("/{id}")
    public String getUser(@PathVariable Long id, Model model) {
        // 业务逻辑
        User user = userService.getUser(id);
        model.addAttribute("user", user);
        return "user/detail";
    }
}

// 5. ViewResolver:视图解析器
// 作用:解析视图名称,返回View对象
public interface ViewResolver {
    
    // 解析视图
    View resolveViewName(String viewName, Locale locale);
}

// 常用实现:InternalResourceViewResolver
// 解析JSP视图
public class InternalResourceViewResolver extends UrlBasedViewResolver {
    
    @Override
    protected View createView(String viewName, Locale locale) {
        // 拼接视图路径
        String url = getPrefix() + viewName + getSuffix();
        // 创建View对象
        return new JstlView(url);
    }
}

// 6. View:视图
// 作用:渲染视图,生成响应
public interface View {
    
    // 渲染视图
    void render(Map<String, ?> model, 
               HttpServletRequest request, 
               HttpServletResponse response);
}

// 常用实现:JstlView(JSP视图)
public class JstlView extends InternalResourceView {
    
    @Override
    protected void renderMergedOutputModel(Map<String, Object> model, 
                                          HttpServletRequest request, 
                                          HttpServletResponse response) {
        // 将模型数据设置到request
        exposeModelAsRequestAttributes(model, request);
        
        // 转发到JSP页面
        RequestDispatcher rd = request.getRequestDispatcher(getUrl());
        rd.forward(request, response);
    }
}

// 7. HandlerInterceptor:拦截器
// 作用:拦截请求,进行预处理和后处理
public interface HandlerInterceptor {
    
    // 前置处理
    boolean preHandle(HttpServletRequest request, 
                     HttpServletResponse response, 
                     Object handler);
    
    // 后置处理
    void postHandle(HttpServletRequest request, 
                   HttpServletResponse response, 
                   Object handler, 
                   ModelAndView modelAndView);
    
    // 完成处理
    void afterCompletion(HttpServletRequest request, 
                        HttpServletResponse response, 
                        Object handler, 
                        Exception ex);
}

回答技巧:

  • 列举核心组件及其作用
  • 说明组件之间的关系
  • 举例说明各组件的使用
  • 画图辅助说明

可能的追问:

  • HandlerMapping有哪些实现类?
  • HandlerAdapter的作用是什么?
  • 如何自定义ViewResolver?

五、MyBatis工作原理(10-15题)

5.1 MyBatis的工作原理是什么?

标准答案:

MyBatis的工作原理包括以下核心流程:

核心要点:

  1. 加载配置:读取mybatis-config.xml和Mapper.xml
  2. 创建SqlSessionFactory:通过SqlSessionFactoryBuilder创建
  3. 创建SqlSession:通过SqlSessionFactory创建
  4. 获取Mapper代理对象:通过SqlSession.getMapper()获取
  5. 执行SQL:通过Mapper接口方法执行SQL
  6. 结果映射:将查询结果映射为Java对象
  7. 关闭SqlSession:释放资源

完整工作流程图:

┌─────────────────────────────────────────────────────────────┐
│              MyBatis完整工作流程                             │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  1. 加载配置文件                                             │
│     - mybatis-config.xml(全局配置)                         │
│     - Mapper.xml(SQL映射文件)                              │
│           ↓                                                  │
│  2. 解析配置文件                                             │
│     - 解析为Configuration对象                                │
│     - 包含所有配置信息和SQL语句                               │
│           ↓                                                  │
│  3. 创建SqlSessionFactory                                    │
│     - SqlSessionFactoryBuilder.build(Configuration)         │
│     - 单例模式,全局唯一                                      │
│           ↓                                                  │
│  4. 创建SqlSession                                           │
│     - SqlSessionFactory.openSession()                       │
│     - 每次请求创建一个                                        │
│           ↓                                                  │
│  5. 获取Mapper代理对象                                        │
│     - SqlSession.getMapper(UserMapper.class)                │
│     - 使用JDK动态代理创建                                     │
│           ↓                                                  │
│  6. 执行Mapper方法                                           │
│     - userMapper.selectById(1L)                             │
│           ↓                                                  │
│  7. 代理对象拦截方法调用                                      │
│     - MapperProxy.invoke()                                  │
│           ↓                                                  │
│  8. 查找对应的MappedStatement                                │
│     - 根据方法名查找SQL语句                                   │
│     - namespace + methodName                                │
│           ↓                                                  │
│  9. 执行SQL语句                                              │
│     - Executor.query()                                      │
│     - 参数处理、SQL拼接、执行查询                             │
│           ↓                                                  │
│  10. 结果映射                                                │
│      - ResultSetHandler处理结果集                            │
│      - 将ResultSet映射为Java对象                             │
│           ↓                                                  │
│  11. 返回结果                                                │
│      - 返回User对象                                          │
│           ↓                                                  │
│  12. 关闭SqlSession                                          │
│      - 释放数据库连接                                         │
│                                                              │
└─────────────────────────────────────────────────────────────┘

核心组件说明:

// 1. SqlSessionFactoryBuilder:构建器
// 作用:创建SqlSessionFactory
public class SqlSessionFactoryBuilder {
    
    public SqlSessionFactory build(InputStream inputStream) {
        // 解析配置文件
        XMLConfigBuilder parser = new XMLConfigBuilder(inputStream);
        Configuration config = parser.parse();
        
        // 创建SqlSessionFactory
        return new DefaultSqlSessionFactory(config);
    }
}

// 2. SqlSessionFactory:会话工厂
// 作用:创建SqlSession
public interface SqlSessionFactory {
    
    // 打开会话
    SqlSession openSession();
    
    // 打开会话(指定是否自动提交)
    SqlSession openSession(boolean autoCommit);
}

// 3. SqlSession:会话
// 作用:执行SQL、获取Mapper、管理事务
public interface SqlSession {
    
    // 查询单个对象
    <T> T selectOne(String statement, Object parameter);
    
    // 查询列表
    <E> List<E> selectList(String statement, Object parameter);
    
    // 插入
    int insert(String statement, Object parameter);
    
    // 更新
    int update(String statement, Object parameter);
    
    // 删除
    int delete(String statement, Object parameter);
    
    // 提交事务
    void commit();
    
    // 回滚事务
    void rollback();
    
    // 获取Mapper
    <T> T getMapper(Class<T> type);
    
    // 关闭会话
    void close();
}

// 4. Executor:执行器
// 作用:执行SQL语句
public interface Executor {
    
    // 查询
    <E> List<E> query(MappedStatement ms, Object parameter, 
                     RowBounds rowBounds, ResultHandler resultHandler);
    
    // 更新
    int update(MappedStatement ms, Object parameter);
    
    // 提交
    void commit(boolean required);
    
    // 回滚
    void rollback(boolean required);
}

// 5. MappedStatement:映射语句
// 作用:封装SQL语句和映射信息
public class MappedStatement {
    private String id;  // namespace + methodName
    private SqlSource sqlSource;  // SQL语句
    private SqlCommandType sqlCommandType;  // SQL类型(SELECT、INSERT等)
    private ResultMap resultMap;  // 结果映射
    // ...
}

// 6. StatementHandler:语句处理器
// 作用:处理JDBC Statement
public interface StatementHandler {
    
    // 准备语句
    Statement prepare(Connection connection);
    
    // 参数化
    void parameterize(Statement statement);
    
    // 查询
    <E> List<E> query(Statement statement, ResultHandler resultHandler);
    
    // 更新
    int update(Statement statement);
}

// 7. ParameterHandler:参数处理器
// 作用:设置SQL参数
public interface ParameterHandler {
    
    // 设置参数
    void setParameters(PreparedStatement ps);
}

// 8. ResultSetHandler:结果集处理器
// 作用:处理查询结果
public interface ResultSetHandler {
    
    // 处理结果集
    <E> List<E> handleResultSets(Statement stmt);
}

完整使用示例:

// 1. mybatis-config.xml配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!-- 环境配置 -->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/test"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    
    <!-- Mapper映射文件 -->
    <mappers>
        <mapper resource="mapper/UserMapper.xml"/>
    </mappers>
</configuration>

// 2. Mapper接口
public interface UserMapper {
    User selectById(Long id);
    List<User> selectAll();
    int insert(User user);
    int update(User user);
    int delete(Long id);
}

// 3. Mapper.xml映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.UserMapper">
    
    <!-- 结果映射 -->
    <resultMap id="BaseResultMap" type="com.example.entity.User">
        <id column="id" property="id"/>
        <result column="username" property="username"/>
        <result column="email" property="email"/>
        <result column="create_time" property="createTime"/>
    </resultMap>
    
    <!-- 查询单个 -->
    <select id="selectById" resultMap="BaseResultMap">
        SELECT id, username, email, create_time
        FROM user
        WHERE id = #{id}
    </select>
    
    <!-- 查询列表 -->
    <select id="selectAll" resultMap="BaseResultMap">
        SELECT id, username, email, create_time
        FROM user
    </select>
    
    <!-- 插入 -->
    <insert id="insert" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO user (username, email, create_time)
        VALUES (#{username}, #{email}, #{createTime})
    </insert>
    
    <!-- 更新 -->
    <update id="update">
        UPDATE user
        SET username = #{username},
            email = #{email}
        WHERE id = #{id}
    </update>
    
    <!-- 删除 -->
    <delete id="delete">
        DELETE FROM user WHERE id = #{id}
    </delete>
</mapper>

// 4. 使用MyBatis
public class MyBatisDemo {
    
    public static void main(String[] args) throws IOException {
        // 1. 加载配置文件
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        
        // 2. 创建SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = 
            new SqlSessionFactoryBuilder().build(inputStream);
        
        // 3. 创建SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        
        try {
            // 4. 获取Mapper代理对象
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            
            // 5. 执行SQL
            User user = userMapper.selectById(1L);
            System.out.println(user);
            
            // 6. 提交事务
            sqlSession.commit();
        } finally {
            // 7. 关闭SqlSession
            sqlSession.close();
        }
    }
}

// 5. Spring集成MyBatis
@Configuration
@MapperScan("com.example.mapper")
public class MyBatisConfig {
    
    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) 
            throws Exception {
        SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
        factory.setDataSource(dataSource);
        factory.setMapperLocations(
            new PathMatchingResourcePatternResolver()
                .getResources("classpath:mapper/*.xml"));
        return factory.getObject();
    }
}

// 6. 使用Mapper(Spring环境)
@Service
public class UserService {
    
    @Autowired
    private UserMapper userMapper;
    
    public User getUser(Long id) {
        return userMapper.selectById(id);
    }
}

Mapper代理对象的创建过程:

// MapperProxy:Mapper代理类
public class MapperProxy<T> implements InvocationHandler {
    
    private final SqlSession sqlSession;
    private final Class<T> mapperInterface;
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) {
        // 1. 如果是Object的方法,直接执行
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
        }
        
        // 2. 获取MapperMethod
        MapperMethod mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
        
        // 3. 执行SQL
        return mapperMethod.execute(sqlSession, args);
    }
}

// MapperMethod:Mapper方法
public class MapperMethod {
    
    public Object execute(SqlSession sqlSession, Object[] args) {
        Object result;
        switch (command.getType()) {
            case INSERT:
                Object param = method.convertArgsToSqlCommandParam(args);
                result = sqlSession.insert(command.getName(), param);
                break;
            case UPDATE:
                Object param = method.convertArgsToSqlCommandParam(args);
                result = sqlSession.update(command.getName(), param);
                break;
            case DELETE:
                Object param = method.convertArgsToSqlCommandParam(args);
                result = sqlSession.delete(command.getName(), param);
                break;
            case SELECT:
                if (method.returnsMany()) {
                    result = executeForMany(sqlSession, args);
                } else {
                    result = sqlSession.selectOne(command.getName(), param);
                }
                break;
            default:
                throw new BindingException("Unknown execution method");
        }
        return result;
    }
}

回答技巧:

  • 按照流程顺序逐步说明
  • 重点说明核心组件的作用
  • 画图辅助说明
  • 举例说明实际使用过程

可能的追问:

  • Mapper接口如何生成代理对象?
  • MyBatis如何处理SQL参数?
  • MyBatis如何映射查询结果?

5.2 MyBatis的缓存机制是什么?

标准答案:

MyBatis提供两级缓存机制:一级缓存和二级缓存。

核心要点:

  1. 一级缓存:SqlSession级别,默认开启,不能关闭
  2. 二级缓存:Mapper级别,需要手动开启
  3. 缓存查询顺序:二级缓存 → 一级缓存 → 数据库
  4. 缓存失效:增删改操作会清空缓存

一级缓存和二级缓存对比:

维度 一级缓存 二级缓存
作用域 SqlSession级别 Mapper级别
生命周期 SqlSession的生命周期 应用的生命周期
默认状态 默认开启,不能关闭 默认关闭,需要手动开启
缓存对象 查询结果对象 查询结果对象的副本
线程安全 线程不安全 线程安全
失效时机 SqlSession关闭或清空 增删改操作

一级缓存示例:

@Test
public void testFirstLevelCache() {
    SqlSession sqlSession = sqlSessionFactory.openSession();
    try {
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        
        // 第一次查询,从数据库查询
        User user1 = userMapper.selectById(1L);
        System.out.println("第一次查询:" + user1);
        
        // 第二次查询,从一级缓存获取
        User user2 = userMapper.selectById(1L);
        System.out.println("第二次查询:" + user2);
        
        // 验证是同一个对象
        System.out.println(user1 == user2);  // true
        
    } finally {
        sqlSession.close();
    }
}

// 输出:
// 第一次查询:User(id=1, username=张三)  // 执行SQL
// 第二次查询:User(id=1, username=张三)  // 从缓存获取
// true

一级缓存失效场景:

@Test
public void testFirstLevelCacheInvalid() {
    SqlSession sqlSession = sqlSessionFactory.openSession();
    try {
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        
        // 场景1:不同的SqlSession
        SqlSession sqlSession2 = sqlSessionFactory.openSession();
        UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
        
        User user1 = userMapper.selectById(1L);  // 查询数据库
        User user2 = userMapper2.selectById(1L);  // 再次查询数据库(不同SqlSession)
        System.out.println(user1 == user2);  // false
        
        // 场景2:执行增删改操作
        User user3 = userMapper.selectById(1L);  // 查询数据库
        userMapper.update(new User(1L, "李四"));  // 更新操作,清空缓存
        User user4 = userMapper.selectById(1L);  // 再次查询数据库
        System.out.println(user3 == user4);  // false
        
        // 场景3:手动清空缓存
        User user5 = userMapper.selectById(1L);  // 查询数据库
        sqlSession.clearCache();  // 清空缓存
        User user6 = userMapper.selectById(1L);  // 再次查询数据库
        System.out.println(user5 == user6);  // false
        
        // 场景4:查询条件不同
        User user7 = userMapper.selectById(1L);  // 查询id=1
        User user8 = userMapper.selectById(2L);  // 查询id=2(不同条件)
        // 两次都查询数据库
        
    } finally {
        sqlSession.close();
    }
}

二级缓存配置:

<!-- 1. mybatis-config.xml中开启二级缓存 -->
<settings>
    <setting name="cacheEnabled" value="true"/>
</settings>

<!-- 2. Mapper.xml中配置缓存 -->
<mapper namespace="com.example.mapper.UserMapper">
    
    <!-- 开启二级缓存 -->
    <cache/>
    
    <!-- 或者自定义缓存配置 -->
    <cache
        eviction="LRU"           <!-- 缓存淘汰策略:LRU、FIFO、SOFT、WEAK -->
        flushInterval="60000"    <!-- 刷新间隔:60秒 -->
        size="512"               <!-- 缓存大小:512个对象 -->
        readOnly="true"/>        <!-- 只读缓存 -->
    
    <!-- SQL语句 -->
    <select id="selectById" resultType="User">
        SELECT * FROM user WHERE id = #{id}
    </select>
</mapper>

二级缓存示例:

// 实体类需要实现Serializable接口
public class User implements Serializable {
    private Long id;
    private String username;
    private String email;
    // ...
}

@Test
public void testSecondLevelCache() {
    // 第一个SqlSession
    SqlSession sqlSession1 = sqlSessionFactory.openSession();
    try {
        UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
        User user1 = userMapper1.selectById(1L);  // 查询数据库
        System.out.println("第一次查询:" + user1);
    } finally {
        sqlSession1.close();  // 关闭SqlSession,数据进入二级缓存
    }
    
    // 第二个SqlSession
    SqlSession sqlSession2 = sqlSessionFactory.openSession();
    try {
        UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
        User user2 = userMapper2.selectById(1L);  // 从二级缓存获取
        System.out.println("第二次查询:" + user2);
    } finally {
        sqlSession2.close();
    }
}

// 输出:
// 第一次查询:User(id=1, username=张三)  // 执行SQL
// 第二次查询:User(id=1, username=张三)  // 从二级缓存获取

缓存查询流程:

┌─────────────────────────────────────────────────────────────┐
│              MyBatis缓存查询流程                             │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  1. 执行查询                                                 │
│           ↓                                                  │
│  2. 查询二级缓存                                             │
│     ├─ 命中 → 返回结果                                       │
│     └─ 未命中 → 继续                                         │
│           ↓                                                  │
│  3. 查询一级缓存                                             │
│     ├─ 命中 → 返回结果                                       │
│     └─ 未命中 → 继续                                         │
│           ↓                                                  │
│  4. 查询数据库                                               │
│           ↓                                                  │
│  5. 将结果放入一级缓存                                        │
│           ↓                                                  │
│  6. SqlSession关闭时,将结果放入二级缓存                      │
│           ↓                                                  │
│  7. 返回结果                                                 │
│                                                              │
└─────────────────────────────────────────────────────────────┘

回答技巧:

  • 先说两级缓存的概念
  • 对比两级缓存的区别
  • 举例说明缓存的使用
  • 说明缓存失效的场景

可能的追问:

  • 为什么需要两级缓存?
  • 二级缓存的淘汰策略有哪些?
  • 如何自定义缓存实现?

六、练习题

6.1 基础练习

练习1:实现一个简单的IOC容器

要求:

  1. 支持Bean的注册和获取
  2. 支持单例和原型作用域
  3. 支持依赖注入

练习2:实现一个简单的AOP代理

要求:

  1. 使用JDK动态代理
  2. 实现方法执行前后的日志记录
  3. 实现方法执行时间统计

练习3:实现一个简单的事务管理器

要求:

  1. 支持事务的开启、提交、回滚
  2. 支持事务的传播行为
  3. 支持异常回滚

6.2 进阶练习

练习4:实现一个简单的DispatcherServlet

要求:

  1. 支持@RequestMapping注解
  2. 支持参数解析
  3. 支持视图渲染

练习5:实现一个简单的MyBatis

要求:

  1. 支持Mapper接口代理
  2. 支持SQL执行
  3. 支持结果映射

6.3 综合练习

练习6:实现一个完整的SSM项目

要求:

  1. 使用Spring IOC管理Bean
  2. 使用Spring AOP实现日志记录
  3. 使用Spring事务管理
  4. 使用SpringMVC处理请求
  5. 使用MyBatis操作数据库

七、学习检查清单

  • 理解Spring IOC容器的工作原理
  • 掌握Bean的生命周期
  • 理解Spring AOP的实现原理
  • 掌握五种通知类型的执行顺序
  • 理解Spring事务管理的实现方式
  • 掌握七种事务传播行为
  • 了解事务失效的场景
  • 理解SpringMVC的请求处理流程
  • 掌握SpringMVC的核心组件
  • 理解MyBatis的工作原理
  • 掌握MyBatis的缓存机制
  • 能够在面试中清晰准确地回答SSM框架相关问题

文档说明:

  • 本文档包含SSM框架原理的核心面试题
  • 每道题都包含标准答案、核心要点、代码示例、回答技巧
  • 建议结合实际项目经验进行学习
  • 面试前重点复习标准答案和核心要点
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐