二十一、Spring Framework 详细知识点文档

Spring Framework 详细知识点文档

目录

  1. [Spring 概述与核心概念](#Spring 概述与核心概念 "#1-spring-%E6%A6%82%E8%BF%B0%E4%B8%8E%E6%A0%B8%E5%BF%83%E6%A6%82%E5%BF%B5")
  2. [IoC 容器与依赖注入](#IoC 容器与依赖注入 "#2-ioc-%E5%AE%B9%E5%99%A8%E4%B8%8E%E4%BE%9D%E8%B5%96%E6%B3%A8%E5%85%A5")
  3. [Bean 的配置与管理](#Bean 的配置与管理 "#3-bean-%E7%9A%84%E9%85%8D%E7%BD%AE%E4%B8%8E%E7%AE%A1%E7%90%86")
  4. [AOP 面向切面编程](#AOP 面向切面编程 "#4-aop-%E9%9D%A2%E5%90%91%E5%88%87%E9%9D%A2%E7%BC%96%E7%A8%8B")
  5. [Spring 事务管理](#Spring 事务管理 "#5-spring-%E4%BA%8B%E5%8A%A1%E7%AE%A1%E7%90%86")
  6. [Spring MVC](#Spring MVC "#6-spring-mvc")
  7. [Spring Boot](#Spring Boot "#7-spring-boot")
  8. [Spring 数据访问](#Spring 数据访问 "#8-spring-%E6%95%B0%E6%8D%AE%E8%AE%BF%E9%97%AE")
  9. [Spring 注解大全](#Spring 注解大全 "#9-spring-%E6%B3%A8%E8%A7%A3%E5%A4%A7%E5%85%A8")
  10. [Spring 设计模式](#Spring 设计模式 "#10-spring-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F")
  11. 最佳实践与常见问题

1. Spring 概述与核心概念

1.1 什么是 Spring Framework?

Spring 是一个开源的轻量级 Java 开发框架,核心是简化企业级应用开发。创建于2003年,由 Rod Johnson 创立。

核心价值主张:

  • 非侵入式:不强制继承特定类或实现特定接口
  • 轻量级:完整的 Spring 框架可以压缩到 1MB 多的 JAR 文件
  • 模块化:按需使用,不强制全部引入

1.2 Spring 模块组成

scss 复制代码
Spring Framework Runtime
├── Core Container (核心容器)
│   ├── spring-core (核心工具类,基础模块)
│   ├── spring-beans (Bean的创建、配置、管理)
│   ├── spring-context (ApplicationContext,IoC容器)
│   ├── spring-expression (SpEL 表达式语言)
│   └── spring-context-support (第三方库集成支持)
│
├── AOP & Instrumentation
│   ├── spring-aop (基于代理的AOP实现)
│   ├── spring-aspects (AspectJ集成)
│   └── spring-instrument (类加载器实现)
│
├── Data Access/Integration
│   ├── spring-jdbc (JDBC抽象层)
│   ├── spring-tx (声明式事务管理)
│   ├── spring-orm (ORM框架集成)
│   ├── spring-oxm (Object/XML映射)
│   └── spring-jms (消息服务)
│
├── Web
│   ├── spring-web (基础Web功能)
│   ├── spring-webmvc (MVC框架)
│   ├── spring-websocket (WebSocket支持)
│   └── spring-webmvc-portlet (Portlet支持)
│
└── Test
    └── spring-test (测试支持)

1.3 Spring 核心概念术语

  • Bean:由 Spring IoC 容器管理的对象
  • IoC (控制反转):将对象创建和依赖关系管理的控制权交给容器
  • DI (依赖注入):IoC 的一种实现方式,容器在运行时动态注入依赖
  • AOP (面向切面编程):将横切关注点与业务逻辑分离
  • 容器:Spring 中负责管理 Bean 的生命周期和配置的核心组件

2. IoC 容器与依赖注入

2.1 IoC 实现原理

IoC 容器的工作流程:

markdown 复制代码
1. 读取配置文件/注解
2. 根据配置元数据创建 Bean 定义 (BeanDefinition)
3. 实例化 Bean
4. 处理依赖注入
5. Bean 初始化 (init-method, InitializingBean接口)
6. Bean 就绪可用
7. 容器关闭时销毁 Bean (destroy-method, DisposableBean接口)

2.2 核心接口

java 复制代码
// 1. BeanFactory - 最基础的 IoC 容器接口
public interface BeanFactory {
    Object getBean(String name) throws BeansException;
    <T> T getBean(String name, Class<T> requiredType);
    <T> T getBean(Class<T> requiredType);
    boolean containsBean(String name);
    boolean isSingleton(String name);
    boolean isPrototype(String name);
    Class<?> getType(String name);
}

// 2. ApplicationContext - 企业级应用上下文
public interface ApplicationContext extends 
    EnvironmentCapable, 
    ListableBeanFactory, 
    HierarchicalBeanFactory,
    MessageSource,           // 国际化
    ApplicationEventPublisher, // 事件发布
    ResourcePatternResolver {  // 资源加载
    // 继承了多个接口,功能更全面
}

// 3. 常用实现类
// - ClassPathXmlApplicationContext (基于XML,类路径)
// - FileSystemXmlApplicationContext (基于XML,文件系统)
// - AnnotationConfigApplicationContext (基于Java配置)
// - WebApplicationContext (Web环境专用)

2.3 依赖注入方式

2.3.1 构造器注入(推荐)
java 复制代码
@Component
public class UserService {
    private final UserRepository userRepository;
    private final EmailService emailService;
    
    // 构造器注入 - Spring 4.3+ 单构造器可不加 @Autowired
    @Autowired
    public UserService(UserRepository userRepository, 
                       EmailService emailService) {
        this.userRepository = userRepository;
        this.emailService = emailService;
    }
}

优点:

  • 保证依赖不可变(final字段)
  • 防止空指针(必须提供依赖)
  • 对单元测试友好
  • 支持循环依赖检测
2.3.2 Setter 注入
java 复制代码
@Component
public class UserService {
    private UserRepository userRepository;
    
    @Autowired
    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}
2.3.3 字段注入(不推荐)
java 复制代码
@Component
public class UserService {
    @Autowired
    private UserRepository userRepository; // 字段注入
}

为什么字段注入不推荐?

  • 隐藏了外部依赖,不利于理解类的职责
  • 无法使用 final 修饰
  • 单元测试困难,需要反射注入
  • 可能导致 NullPointerException
2.3.4 方法注入
java 复制代码
// Lookup 方法注入 - 解决单例依赖原型的问题
@Component
@Scope("singleton")
public abstract class SingletonBean {
    
    @Lookup
    public abstract PrototypeBean getPrototypeBean();
    
    public void process() {
        PrototypeBean pb = getPrototypeBean(); // 每次获取新的实例
    }
}

2.4 @Autowired 详细机制

java 复制代码
// 1. 按类型装配(默认)
@Autowired
private UserDao userDao;

// 2. 配合 @Qualifier 按名称
@Autowired
@Qualifier("userDaoImpl1")
private UserDao userDao;

// 3. required 属性
@Autowired(required = false) // 找不到bean也不报错
private Optional<Service> service;

// 4. 注入集合
@Autowired
private List<UserDao> userDaoList; // 注入所有实现

@Autowired
private Map<String, UserDao> userDaoMap; // beanName -> bean

// 5. @Primary 指定首选
@Component
@Primary
public class PrimaryUserDao implements UserDao { }

2.5 @Resource vs @Autowired

特性 @Autowired @Resource
来源 Spring JSR-250 (JDK标准)
默认装配方式 byType byName
属性 required name, type
可标记位置 构造器, 字段, 方法, 参数 字段, setter

3. Bean 的配置与管理

3.1 Bean 作用域 (Scope)

3.1.1 Singleton(默认)
java 复制代码
@Component
@Scope("singleton")
// 或 @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public class SingletonBean {
    // 整个容器中只有一个实例
    // Spring默认使用单例注册表实现
}

实现原理: Spring 使用 ConcurrentHashMap 缓存单例 bean,key 为 beanName。

3.1.2 Prototype
java 复制代码
@Component
@Scope("prototype")
// 或 @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PrototypeBean {
    // 每次请求创建新实例
    // 注意: Spring不管理prototype bean的完整生命周期
}
3.1.3 Web 作用域
java 复制代码
// Request 作用域 - 每个HTTP请求一个实例
@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestBean { }

// Session 作用域 - 每个HTTP会话一个实例
@Component
@Scope(value = "session", proxyMode = ScopedProxyMode.INTERFACES)
public class SessionBean { }

// Application 作用域 - 整个ServletContext一个实例
@Component
@Scope("application")
public class ApplicationBean { }

// WebSocket 作用域
@Component
@Scope("websocket")
public class WebSocketBean { }

3.2 Bean 生命周期详解

java 复制代码
@Component
public class LifecycleBean implements 
    InitializingBean,         // afterPropertiesSet()
    DisposableBean,           // destroy()
    BeanNameAware,           // setBeanName()
    BeanFactoryAware,        // setBeanFactory()
    ApplicationContextAware {  // setApplicationContext()
    
    private String name;
    
    public LifecycleBean() {
        System.out.println("1. 实例化 - 构造方法");
    }
    
    @Autowired
    public void setDependency(UserService service) {
        System.out.println("2. 属性注入");
    }
    
    @Override
    public void setBeanName(String name) {
        this.name = name;
        System.out.println("3. BeanNameAware - " + name);
    }
    
    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
        System.out.println("4. BeanFactoryAware");
    }
    
    @Override
    public void setApplicationContext(ApplicationContext ctx) {
        System.out.println("5. ApplicationContextAware");
    }
    
    @PostConstruct  // JSR-250
    public void postConstruct() {
        System.out.println("6. @PostConstruct(优先于afterPropertiesSet)");
    }
    
    @Override
    public void afterPropertiesSet() {
        System.out.println("7. afterPropertiesSet");
    }
    
    // 自定义 init-method / Xml 配置
    public void customInit() {
        System.out.println("8. 自定义 init-method");
    }
    
    // Bean 就绪...
    
    @PreDestroy
    public void preDestroy() {
        System.out.println("9. @PreDestroy");
    }
    
    @Override
    public void destroy() {
        System.out.println("10. DisposableBean.destroy()");
    }
    
    public void customDestroy() {
        System.out.println("11. 自定义 destroy-method");
    }
}

完整生命周期阶段:

css 复制代码
[实例化] → [属性填充] → [Aware接口] → [BeanPostProcessor前置处理]
→ [初始化回调] → [BeanPostProcessor后置处理] → [就绪] → [销毁]

3.3 BeanPostProcessor

java 复制代码
@Component
public class CustomBeanPostProcessor implements BeanPostProcessor {
    
    // 初始化之前调用
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        if (bean instanceof CustomBean) {
            System.out.println("前置处理:" + beanName);
        }
        return bean; // 可以返回代理对象
    }
    
    // 初始化之后调用
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        if (bean instanceof CustomBean) {
            System.out.println("后置处理:" + beanName);
        }
        return bean; // AOP生成代理的关键位置
    }
}

3.4 Bean 配置方式

3.4.1 XML 配置
xml 复制代码
<beans>
    <!-- 构造器注入 -->
    <bean id="userDao" class="com.example.UserDao"/>
    
    <bean id="userService" class="com.example.UserService">
        <constructor-arg ref="userDao"/>
        <constructor-arg value="admin"/>
    </bean>
    
    <!-- Setter 注入 -->
    <bean id="emailService" class="com.example.EmailService">
        <property name="smtpServer" value="smtp.example.com"/>
        <property name="port" value="587"/>
    </bean>
    
    <!-- 集合注入 -->
    <bean id="complexService" class="com.example.ComplexService">
        <property name="configList">
            <list>
                <value>config1</value>
                <value>config2</value>
            </list>
        </property>
        <property name="configMap">
            <map>
                <entry key="key1" value-ref="beanRef1"/>
                <entry key="key2" value="value2"/>
            </map>
        </property>
    </bean>
</beans>
3.4.2 Java 注解配置
java 复制代码
@Configuration
@ComponentScan(basePackages = "com.example")
public class AppConfig {
    
    @Bean
    @Scope("singleton")
    @Lazy // 延迟初始化
    public DataSource dataSource() {
        HikariDataSource ds = new HikariDataSource();
        ds.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
        ds.setUsername("root");
        ds.setPassword("password");
        return ds;
    }
    
    @Bean
    public JdbcTemplate jdbcTemplate(@Qualifier("dataSource") DataSource ds) {
        return new JdbcTemplate(ds);
    }
}

3.5 循环依赖

java 复制代码
// A依赖B,B依赖A - 循环依赖
@Component
public class A {
    @Autowired
    private B b;
}

@Component
public class B {
    @Autowired
    private A a;
}

Spring 解决循环依赖的三级缓存:

java 复制代码
// 一级缓存:singletonObjects - 完全初始化完成的单例Bean
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

// 二级缓存:earlySingletonObjects - 提前暴露的单例Bean(未完全初始化)
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

// 三级缓存:singletonFactories - Bean的工厂对象
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

解决流程:

  1. 创建 A 实例 → 放入三级缓存
  2. 填充 A 的 b 属性 → 发现需要 B
  3. 创建 B 实例 → 放入三级缓存
  4. 填充 B 的 a 属性 → 从三级缓存获取 A 的早期引用
  5. B 完成初始化 → 放入一级缓存
  6. A 获取完整的 B → A 完成初始化

注意: 构造器注入无法解决循环依赖(因为无法获取早期引用),会抛出 BeanCurrentlyInCreationException


4. AOP 面向切面编程

4.1 AOP 核心概念

scss 复制代码
┌─────────────────────────────────────────┐
│            AOP 概念图解                    │
├─────────────────────────────────────────┤
│                                          │
│  JoinPoint (连接点)                       │
│  └── 程序执行过程中的点(方法调用等)       │
│                                          │
│  Pointcut (切入点)                        │
│  └── 匹配连接点的表达式                   │
│                                         │
│  Advice (通知/增强)                       │
│  └── 在切入点上执行的操作                 │
│                                         │
│  Aspect (切面)                           │
│  └── 切入点 + 通知的组合                  │
│                                         │
│  Weaving (织入)                          │
│  └── 将切面应用到目标对象的过程           │
│                                         │
│  Target (目标对象)                        │
│  └── 被代理的对象                        │
│                                         │
│  Introduction (引入)                      │
│  └── 动态添加方法/接口                    │
│                                         │
└─────────────────────────────────────────┘

4.2 通知类型 (Advice)

java 复制代码
@Aspect
@Component
public class LoggingAspect {
    
    // 1. @Before - 前置通知
    @Before("execution(* com.example.service.*.*(..))")
    public void before(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("调用方法:" + methodName + ",参数:" + Arrays.toString(args));
    }
    
    // 2. @AfterReturning - 返回通知(成功返回后)
    @AfterReturning(
        pointcut = "execution(* com.example.service.*.*(..))",
        returning = "result"
    )
    public void afterReturning(JoinPoint joinPoint, Object result) {
        System.out.println("方法返回:" + result);
    }
    
    // 3. @AfterThrowing - 异常通知
    @AfterThrowing(
        pointcut = "execution(* com.example.service.*.*(..))",
        throwing = "ex"
    )
    public void afterThrowing(JoinPoint joinPoint, Exception ex) {
        System.out.println("方法异常:" + ex.getMessage());
    }
    
    // 4. @After - 最终通知(类似finally)
    @After("execution(* com.example.service.*.*(..))")
    public void after(JoinPoint joinPoint) {
        System.out.println("方法结束");
    }
    
    // 5. @Around - 环绕通知(最强大)
    @Around("execution(* com.example.service.*.*(..))")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();
        
        System.out.println("环绕前:" + pjp.getSignature().getName());
        
        Object result;
        try {
            result = pjp.proceed(); // 执行目标方法
        } catch (Exception e) {
            System.out.println("环绕异常:" + e.getMessage());
            throw e;
        }
        
        long end = System.currentTimeMillis();
        System.out.println("环绕后,耗时:" + (end - start) + "ms");
        
        return result;
    }
}

4.3 切入点表达式 (Pointcut)

java 复制代码
@Aspect
@Component
public class PointcutExamples {
    
    // 1. execution - 最常用
    @Pointcut("execution(public * com.example.service.UserService.*(..))")
    public void userServiceMethods() {}
    
    // execution 语法分解:
    // execution(
    //   [修饰符] 返回类型 [包.类.]方法名(参数列表) [throws 异常]
    // )
    
    // 通配符说明:
    // *    - 匹配任意字符(不包括包分隔符 .)
    // ..   - 匹配任意字符(包括包分隔符),匹配任意参数
    // +    - 匹配类及其所有子类
    
    // 示例:
    @Pointcut("execution(* com.example..*.*(..))")  // com.example包及子包的所有方法
    public void allMethodsInPackage() {}
    
    @Pointcut("execution(* com.example.service.*.find*(..))")  // service包下所有find开头的方法
    public void finderMethods() {}
    
    @Pointcut("execution(* com.example..*(..)) && within(com.example.service..*)")
    public void combinedPointcut() {}
    
    // 2. within - 限制类型匹配
    @Pointcut("within(com.example.service.*)")
    public void serviceLayer() {}
    
    // 3. this - 代理对象类型匹配
    @Pointcut("this(com.example.service.UserService)")
    public void proxyIsUserService() {}
    
    // 4. target - 目标对象类型匹配
    @Pointcut("target(com.example.repository.BaseRepository)")
    public void targetIsRepository() {}
    
    // 5. args - 参数类型匹配
    @Pointcut("args(Long, ..)")
    public void firstArgIsLong() {}
    
    // 6. @annotation - 方法注解匹配
    @Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)")
    public void transactionalMethods() {}
    
    // 7. @within - 类注解匹配
    @Pointcut("@within(org.springframework.stereotype.Service)")
    public void serviceClassMethods() {}
    
    // 8. @args - 参数注解匹配
    @Pointcut("@args(com.example.annotation.Validated)")
    public void validatedArgs() {}
    
    // 9. bean - Bean名称匹配
    @Pointcut("bean(userService)")  // 名称匹配
    public void userServiceBean() {}
    
    @Pointcut("bean(*Service)")    // 通配符匹配
    public void allServiceBeans() {}
}

4.4 AOP 实现原理

4.4.1 JDK 动态代理
java 复制代码
// 基于接口的代理
public class JdkProxyDemo {
    
    public static Object createProxy(Object target) {
        return Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            (proxy, method, args) -> {
                System.out.println("JDK代理 - 前置增强");
                Object result = method.invoke(target, args);
                System.out.println("JDK代理 - 后置增强");
                return result;
            }
        );
    }
}
4.4.2 CGLIB 代理
java 复制代码
// 基于子类的代理
public class CglibProxyDemo {
    
    public static Object createProxy(Class<?> targetClass) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(targetClass);
        enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
            System.out.println("CGLIB代理 - 前置增强");
            Object result = proxy.invokeSuper(obj, args);
            System.out.println("CGLIB代理 - 后置增强");
            return result;
        });
        return enhancer.create();
    }
}
4.4.3 Spring 代理选择策略
java 复制代码
// 默认策略
// 1. 如果目标实现了接口 → 使用JDK动态代理
// 2. 如果目标没有实现接口 → 使用CGLIB代理
// 3. Spring Boot 2.x 开始默认使用CGLIB代理(proxyTargetClass = true)

@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true) // 强制使用CGLIB
public class AopConfig { }

JDK vs CGLIB 对比:

特性 JDK 动态代理 CGLIB
代理方式 基于接口 基于继承
限制 目标必须实现接口 不能代理final类/方法
性能 创建快,执行稍慢 创建慢,执行快
包位置 java.lang.reflect org.springframework.cglib

4.5 多切面执行顺序

java 复制代码
@Aspect
@Component
@Order(1)  // 优先级最高
public class FirstAspect {
    @Around("execution(* com.example..*(..))")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("Order 1 - Before");
        Object result = pjp.proceed();
        System.out.println("Order 1 - After");
        return result;
    }
}

@Aspect
@Component
@Order(2)
public class SecondAspect {
    @Around("execution(* com.example..*(..))")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("Order 2 - Before");
        Object result = pjp.proceed();
        System.out.println("Order 2 - After");
        return result;
    }
}

// 执行结果:
// Order 1 - Before
// Order 2 - Before
// 目标方法...
// Order 2 - After
// Order 1 - After
// (类似同心圆:Order小的在最外层)

5. Spring 事务管理

5.1 事务核心接口

java 复制代码
// 1. PlatformTransactionManager - 事务管理器
public interface PlatformTransactionManager {
    TransactionStatus getTransaction(TransactionDefinition definition);
    void commit(TransactionStatus status);
    void rollback(TransactionStatus status);
}

// 实现类:
// DataSourceTransactionManager - JDBC
// JpaTransactionManager - JPA
// HibernateTransactionManager - Hibernate
// JtaTransactionManager - 分布式事务

// 2. TransactionDefinition - 事务定义
public interface TransactionDefinition {
    int getPropagationBehavior();   // 传播行为
    int getIsolationLevel();       // 隔离级别
    int getTimeout();              // 超时时间
    boolean isReadOnly();          // 是否只读
    String getName();              // 事务名称
}

// 3. TransactionStatus - 事务状态
public interface TransactionStatus {
    boolean isNewTransaction();
    boolean hasSavepoint();
    void setRollbackOnly();
    boolean isRollbackOnly();
    boolean isCompleted();
}

5.2 声明式事务(推荐)

java 复制代码
@Service
public class OrderService {
    
    @Autowired
    private OrderRepository orderRepository;
    
    @Autowired
    private InventoryService inventoryService;
    
    // 基本用法
    @Transactional
    public void createOrder(Order order) {
        orderRepository.save(order);
        inventoryService.reduceStock(order.getProductId(), order.getQuantity());
    }
    
    // 详细配置
    @Transactional(
        propagation = Propagation.REQUIRED,    // 传播行为
        isolation = Isolation.READ_COMMITTED,   // 隔离级别
        timeout = 30,                           // 超时秒数
        readOnly = false,                       // 非只读
        rollbackFor = Exception.class,          // 触发回滚的异常
        noRollbackFor = ValidationException.class // 不触发回滚的异常
    )
    public void createOrderDetailed(Order order) {
        // 事务操作
    }
}

5.3 事务传播行为

java 复制代码
public enum Propagation {
    // 1. REQUIRED (默认) - 有则加入,无则新建
    @Transactional(propagation = Propagation.REQUIRED)
    
    // 2. REQUIRES_NEW - 总是新建事务,挂起当前事务
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    
    // 3. SUPPORTS - 有则加入,无则非事务运行
    @Transactional(propagation = Propagation.SUPPORTS)
    
    // 4. NOT_SUPPORTED - 非事务运行,挂起当前事务
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    
    // 5. MANDATORY - 必须在事务中,否则抛异常
    @Transactional(propagation = Propagation.MANDATORY)
    
    // 6. NEVER - 不能在事务中,否则抛异常
    @Transactional(propagation = Propagation.NEVER)
    
    // 7. NESTED - 嵌套事务(需要JDBC Savepoint支持)
    @Transactional(propagation = Propagation.NESTED)
}

传播行为对比表:

传播行为 当前无事务 当前有事务 备注
REQUIRED 新建 加入 默认,最常用
REQUIRES_NEW 新建 新建(挂起) 独立事务
SUPPORTS 非事务 加入 灵活
NOT_SUPPORTED 非事务 挂起 不参与事务
MANDATORY 抛异常 加入 强制事务
NEVER 非事务 抛异常 不允许事务
NESTED 新建 嵌套 部分回滚

5.4 事务隔离级别

java 复制代码
public enum Isolation {
    // 1. DEFAULT - 使用数据库默认级别
    DEFAULT(-1),
    
    // 2. READ_UNCOMMITTED - 读未提交(最低级别)
    // 问题:脏读、不可重复读、幻读
    READ_UNCOMMITTED(1),
    
    // 3. READ_COMMITTED - 读已提交
    // 问题:不可重复读、幻读
    READ_COMMITTED(2),
    
    // 4. REPEATABLE_READ - 可重复读
    // 问题:幻读(MySQL InnoDB 通过MVCC避免了幻读)
    REPEATABLE_READ(4),
    
    // 5. SERIALIZABLE - 串行化(最高级别)
    // 问题:无,但性能最差
    SERIALIZABLE(8)
}

5.5 事务失效场景

java 复制代码
@Service
public class TransactionPitfalls {
    
    // ❌ 场景1:非public方法
    @Transactional
    private void privateMethod() { } // 事务不生效
    
    @Transactional
    protected void protectedMethod() { } // 事务不生效
    
    // ❌ 场景2:同类方法调用(自调用)
    @Transactional
    public void methodA() {
        this.methodB(); // 事务不生效!因为绕过了代理
    }
    
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void methodB() {
        // 没有被代理拦截
    }
    
    // ✅ 解决方案:注入自己(Spring 4.3+)
    @Autowired
    private TransactionPitfalls self;
    
    @Transactional
    public void methodC() {
        self.methodB(); // 通过代理调用,事务生效
    }
    
    // ✅ 或使用AopContext
    public void methodD() {
        ((TransactionPitfalls) AopContext.currentProxy()).methodB();
    }
    
    // ❌ 场景3:异常被捕获
    @Transactional
    public void methodE() {
        try {
            // 数据库操作
            throw new RuntimeException("error");
        } catch (Exception e) {
            log.error("异常被捕获,事务不会回滚");
        }
    }
    
    // ✅ 正确做法:手动回滚
    @Transactional
    public void methodF() {
        try {
            // 数据库操作
        } catch (Exception e) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            throw e;
        }
    }
    
    // ❌ 场景4:checked异常默认不回滚
    @Transactional // 默认只回滚RuntimeException和Error
    public void methodG() throws Exception {
        throw new Exception("checked异常,默认不回滚");
    }
    
    // ✅ 指定rollbackFor
    @Transactional(rollbackFor = Exception.class)
    public void methodH() throws Exception {
        throw new Exception("指定回滚checked异常");
    }
}

6. Spring MVC

6.1 核心架构

scss 复制代码
请求处理流程:
┌──────────────┐     ┌────────────────┐     ┌──────────────────┐
│  Dispatcher  │ ──→ │ HandlerMapping │ ──→ │ Controller       │
│  Servlet     │     │                │     │ (Handler)         │
│  (前端控制器)│     │ 返回处理器链    │     │ 执行业务逻辑      │
└──────┬───────┘     └────────────────┘     └────────┬─────────┘
       │                                             │
       │                                             ↓
       │                  ┌──────────────┐    ┌──────────────┐
       │                  │ ViewResolver │←── │ ModelAndView │
       │                  │ 解析视图     │    │ 返回视图模型  │
       │                  └──────┬───────┘    └──────────────┘
       │                         │
       ↓                         ↓
  ┌──────────┐            ┌───────────┐
  │ Response │←───────────│ View      │
  │          │   渲染      │ 渲染视图  │
  └──────────┘            └───────────┘

6.2 DispatcherServlet 详解

java 复制代码
// 前端控制器 - Spring MVC 的核心
@WebServlet(urlPatterns = "/")
public class DispatcherServlet extends FrameworkServlet {
    
    // 初始化过程
    @Override
    protected void initStrategies(ApplicationContext context) {
        initMultipartResolver(context);      // 文件上传解析器
        initLocaleResolver(context);         // 国际化解析器
        initThemeResolver(context);          // 主题解析器
        initHandlerMappings(context);        // 处理器映射器
        initHandlerAdapters(context);        // 处理器适配器
        initHandlerExceptionResolvers(context); // 异常解析器
        initRequestToViewNameTranslator(context); // 视图名翻译器
        initViewResolvers(context);          // 视图解析器
        initFlashMapManager(context);        // FlashMap管理器
    }
    
    // 核心请求处理方法
    @Override
    protected void doDispatch(HttpServletRequest request, 
                              HttpServletResponse response) throws Exception {
        // 1. 获取处理器执行链
        HandlerExecutionChain handler = getHandler(request);
        
        // 2. 获取处理器适配器
        HandlerAdapter adapter = getHandlerAdapter(handler.getHandler());
        
        // 3. 执行拦截器前置方法
        handler.applyPreHandle(request, response);
        
        // 4. 实际执行处理器
        ModelAndView mv = adapter.handle(request, response, handler.getHandler());
        
        // 5. 视图解析和渲染
        processDispatchResult(request, response, handler, mv);
        
        // 6. 执行拦截器后置方法
        handler.applyPostHandle(request, response, mv);
    }
}

6.3 Controller 编写

java 复制代码
@RestController  // = @Controller + @ResponseBody
@RequestMapping("/api/users")
@Validated
public class UserController {
    
    @Autowired
    private UserService userService;
    
    // GET 请求 - 获取资源
    @GetMapping("/{id}")
    public ResponseEntity<User> getUser(@PathVariable Long id) {
        User user = userService.findById(id);
        return ResponseEntity.ok(user);
    }
    
    // POST 请求 - 创建资源
    @PostMapping
    public ResponseEntity<User> createUser(@Valid @RequestBody UserDTO dto) {
        User user = userService.create(dto);
        URI location = ServletUriComponentsBuilder
            .fromCurrentRequest()
            .path("/{id}")
            .buildAndExpand(user.getId())
            .toUri();
        return ResponseEntity.created(location).body(user);
    }
    
    // PUT 请求 - 更新资源
    @PutMapping("/{id}")
    public ResponseEntity<User> updateUser(
            @PathVariable Long id, 
            @Valid @RequestBody UserDTO dto) {
        User user = userService.update(id, dto);
        return ResponseEntity.ok(user);
    }
    
    // DELETE 请求 - 删除资源
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
        userService.delete(id);
        return ResponseEntity.noContent().build();
    }
    
    // PATCH 请求 - 部分更新
    @PatchMapping("/{id}")
    public ResponseEntity<User> patchUser(
            @PathVariable Long id, 
            @RequestBody Map<String, Object> updates) {
        User user = userService.patch(id, updates);
        return ResponseEntity.ok(user);
    }
    
    // 请求参数
    @GetMapping
    public ResponseEntity<Page<User>> listUsers(
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "20") int size,
            @RequestParam(required = false) String name,
            @RequestParam(required = false) String sort) {
        // ...
    }
    
    // 矩阵变量 (需手动配置启用)
    @GetMapping("/{id}/orders/{orderId}")
    public ResponseEntity<Order> getOrder(
            @PathVariable Long id,
            @MatrixVariable(pathVar = "orderId") String q) {
        // ...
    }
}

6.4 拦截器 (Interceptor)

java 复制代码
@Component
public class AuthInterceptor implements HandlerInterceptor {
    
    // 前置处理 - Controller执行
相关推荐
马艳泽1 小时前
Maven 编译时生成、纯静态文档、不能调试、零侵入、不用运行项目的api文档
后端
泰式大师1 小时前
从“记忆”到“项目 Wiki”:我在 SkillLite 里实现了一套 Markdown-only LLM Wiki 自动维护机制
后端
渐儿1 小时前
案例2:内存管理与性能优化
后端
一叶之政1 小时前
C++ 系统学习日记・第 09 天|指针全解:定义 + 内存 + 空 / 野指针 + const 修饰 + 数组 + 函数
后端
风曦Kisaki1 小时前
# Linux服务Day1:模板机制作、FTP与NTP服务配置全解析
后端
渐儿1 小时前
案例3:文件系统与数据持久化
后端
渐儿1 小时前
Git 高阶使用与实战场景指南
后端
渐儿2 小时前
AI算法基础常识 - 从原理到应用
后端
zach2 小时前
Nginx 反向代理后端接口,一步步解决跨域冲突、预检报错问题
后端