吃透 Spring 体系结构

简介: 本文深入剖析Spring框架的体系结构,围绕IOC(控制反转)和AOP(面向切面编程)两大核心思想,系统讲解了核心容器、AOP、数据访问与Web模块的原理及实战应用,并辨析了常见技术点差异,助力开发者掌握其设计精髓。

在Java企业级开发领域,Spring框架无疑是当之无愧的基石。无论是微服务架构、分布式系统,还是传统的单体应用,Spring都以其强大的扩展性、灵活性和易用性,成为开发者的首选框架。但很多开发者在使用Spring时,往往停留在“API调用”层面,对其底层体系结构、核心设计思想一知半解,导致遇到复杂问题时难以定位,更无法充分发挥Spring的强大能力。

一、Spring体系结构整体概览

Spring并非单一框架,而是一个庞大的生态体系,核心围绕“简化Java开发”这一目标展开。其整体架构采用分层设计和模块化拆分,既保证了核心功能的简洁性,又通过扩展模块满足不同场景的需求。

1.1 核心架构图

image.png

1.2 架构核心设计思想

Spring的所有功能都构建在两大核心思想之上,这也是理解整个Spring体系的关键:

  1. 控制反转(IOC):将对象的创建、依赖管理等控制权从业务代码转移到Spring容器,实现“依赖注入”,降低代码耦合度。
  2. 面向切面编程(AOP):在不修改原有代码的前提下,通过动态代理为代码添加通用功能(如日志、事务、权限校验),实现关注点分离。

这两大思想贯穿于Spring的所有核心模块,是Spring生态的“灵魂”。后续所有模块的解析,都会围绕这两个核心展开。

二、核心容器模块:Spring的“心脏”

核心容器(Core Container)是Spring的基础,负责对象的创建、管理和依赖注入,是所有其他模块的支撑。包含Beans、Core、Context、Expression Language四个子模块。

2.1 Core模块:核心工具类

Core模块提供了Spring框架的基础工具类,是整个Spring的“工具包”,其他模块都依赖于它。核心功能包括:

  • 资源访问(Resource接口,统一文件、URL、类路径等资源的访问方式)
  • 类型转换(TypeConverter接口,实现不同类型之间的安全转换)
  • 日志适配(整合JCL、SLF4J等日志框架,提供统一的日志接口)

实战示例:Resource资源访问

package com.jam.demo.core;

import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.util.FileCopyUtils;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

/**
* Spring Core模块资源访问示例
* @author ken
*/

@Slf4j
public class ResourceDemo {
   public static void main(String[] args) {
       // 访问类路径下的配置文件
       Resource resource = new ClassPathResource("application.properties");
       try {
           // 使用Spring工具类读取文件内容
           byte[] content = FileCopyUtils.copyToByteArray(resource.getInputStream());
           String contentStr = new String(content, StandardCharsets.UTF_8);
           log.info("配置文件内容:\n{}", contentStr);
       } catch (IOException e) {
           log.error("读取配置文件失败", e);
       }
   }
}

2.2 Beans模块:Bean的核心管理

Beans模块是IOC思想的核心实现,负责Bean的定义、创建、注册和管理。核心概念包括:

  • BeanDefinition:描述Bean的元数据(类名、属性、依赖、作用域等)
  • BeanFactory:Bean容器的顶层接口,提供Bean的获取、创建功能
  • BeanWrapper:对Bean对象进行包装,提供属性设置、类型转换等功能

核心底层逻辑

  1. 开发者通过XML、注解(@Component、@Bean)等方式定义Bean;
  2. Spring解析这些定义,生成BeanDefinition对象;
  3. BeanFactory根据BeanDefinition创建Bean实例(默认单例,懒加载);
  4. 通过BeanWrapper为Bean设置属性和依赖。

实战示例:手动创建BeanFactory管理Bean

package com.jam.demo.beans;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;

/**
* BeanFactory手动管理Bean示例
* @author ken
*/

@Slf4j
public class BeanFactoryDemo {
   public static void main(String[] args) {
       // 1. 创建BeanFactory实例(默认实现类)
       DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
       
       // 2. 定义BeanDefinition(描述UserService)
       BeanDefinition userServiceDefinition = new RootBeanDefinition(UserService.class);
       
       // 3. 向BeanFactory注册Bean
       beanFactory.registerBeanDefinition("userService", userServiceDefinition);
       
       // 4. 从BeanFactory获取Bean(此时才创建Bean实例,懒加载)
       UserService userService = beanFactory.getBean("userService", UserService.class);
       userService.sayHello("Spring Beans");
       
       // 5. 验证单例特性(多次获取为同一实例)
       UserService userService2 = beanFactory.getBean("userService", UserService.class);
       log.info("是否为同一实例:{}", userService == userService2);
   }
   
   /**
    * 测试Bean类
    * @author ken
    */

   @Data
   public static class UserService {
       public void sayHello(String name) {
           log.info("Hello, {}! 这是BeanFactory创建的Bean", name);
       }
   }
}

2.3 Context模块:应用上下文

Context模块基于Core和Beans模块,提供了更强大的应用上下文管理功能,是BeanFactory的增强版。核心优势:

  • 非懒加载:默认启动时创建所有单例Bean(BeanFactory默认懒加载)
  • 支持国际化、资源访问、事件发布等高级功能
  • 提供多种实现类,适配不同环境(ClassPathXmlApplicationContext、AnnotationConfigApplicationContext等)

核心接口关系


实战示例:Ann image.png otationConfigApplicationContext使用(注解驱动)

package com.jam.demo.context;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
* 注解驱动的应用上下文示例
* @author ken
*/

@Slf4j
public class AnnotationContextDemo {
   public static void main(String[] args) {
       // 1. 创建注解驱动的应用上下文,扫描指定包下的注解Bean
       AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
       
       // 2. 获取Bean(UserService被@Bean注解定义)
       UserService userService = context.getBean(UserService.class);
       userService.queryUser("1001");
       
       // 3. 关闭上下文
       context.close();
   }
   
   /**
    * 应用配置类,用于定义Bean
    * @author ken
    */

   @Configuration
   public static class AppConfig {
       /**
        * 定义UserService Bean
        * @return UserService实例
        */

       @Bean
       public UserService userService() {
           return new UserService();
       }
   }
   
   /**
    * 用户服务类
    * @author ken
    */

   public static class UserService {
       public void queryUser(String userId) {
           log.info("查询用户信息,用户ID:{}", userId);
       }
   }
}

2.4 Expression Language模块:Spring表达式语言

Spring EL(SpEL)是一种强大的表达式语言,支持在运行时查询和操作对象。核心功能包括:

  • 访问对象属性和方法
  • 执行算术运算、逻辑运算
  • 读取配置文件、系统属性
  • 集合操作、正则表达式匹配

实战示例:SpEL表达式使用

package com.jam.demo.el;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.util.ArrayList;
import java.util.List;

/**
* Spring EL表达式示例
* @author ken
*/

@Slf4j
public class Speldemo {
   public static void main(String[] args) {
       // 1. 创建SpEL解析器
       ExpressionParser parser = new SpelExpressionParser();
       
       // 2. 简单表达式:算术运算
       Expression exp1 = parser.parseExpression("100 + 200 * 3");
       Integer result1 = exp1.getValue(Integer.class);
       log.info("算术运算结果:{}", result1);
       
       // 3. 访问对象属性和方法
       User user = new User("张三", 25);
       StandardEvaluationContext context = new StandardEvaluationContext(user);
       
       Expression exp2 = parser.parseExpression("name");
       String userName = exp2.getValue(context, String.class);
       log.info("用户姓名:{}", userName);
       
       Expression exp3 = parser.parseExpression("getName().toUpperCase()");
       String upperName = exp3.getValue(context, String.class);
       log.info("姓名大写:{}", upperName);
       
       // 4. 集合操作
       List<String> list = new ArrayList<>();
       list.add("Spring");
       list.add("Spring Boot");
       list.add("Spring Cloud");
       context.setVariable("list", list);
       
       Expression exp4 = parser.parseExpression("#list.stream().filter(s -> s.startsWith('Spring ')).collect(toList())");
       List<String> resultList = exp4.getValue(context, List.class);
       log.info("集合过滤结果:{}", resultList);
   }
   
   /**
    * 测试用户类
    * @author ken
    */

   @Data
   public static class User {
       private String name;
       private Integer age;
       
       public User(String name, Integer age) {
           this.name = name;
           this.age = age;
       }
   }
}

三、AOP模块:面向切面编程的实现

AOP模块是Spring实现面向切面编程的核心,通过动态代理技术,在不修改原有代码的前提下,为程序添加通用功能(如日志、事务、权限)。核心依赖于Core和Beans模块。

3.1 AOP核心概念

  • 切面(Aspect):封装通用功能的类(如日志切面、事务切面)
  • 连接点(JoinPoint):程序执行过程中的特定点(如方法调用、属性赋值)
  • 切入点(Pointcut):通过表达式筛选出的需要增强的连接点
  • 通知(Advice):切面在连接点执行的具体操作(前置、后置、环绕、异常、最终通知)
  • 目标对象(Target):被增强的对象
  • 代理对象(Proxy):Spring为目标对象创建的代理对象,用于执行增强逻辑

3.2 底层实现原理

Spring AOP默认使用JDK动态代理(针对接口),如果目标对象没有实现接口,则使用CGLIB动态代理(通过继承目标类)。核心流程:

  1. 开发者定义切面(@Aspect)和切入点(@Pointcut);
  2. Spring解析切面配置,生成Advisor(包含切入点和通知);
  3. 容器创建目标对象时,根据Advisor判断是否需要增强;
  4. 若需要增强,通过动态代理创建代理对象;
  5. 调用目标方法时,先执行代理对象的增强逻辑,再执行目标方法。

AOP执行流程

image.png

3.3 实战示例:AOP日志增强(结合最新Spring AOP)

1. 依赖配置(pom.xml)

<!-- Spring AOP核心依赖 -->
<dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-aop</artifactId>
   <version>6.1.5</version>
</dependency>
<!-- AspectJ支持(Spring AOP依赖AspectJ表达式) -->
<dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjweaver</artifactId>
   <version>1.9.21</version>
</dependency>
<!-- Lombok -->
<dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok</artifactId>
   <version>1.18.30</version>
   <scope>provided</scope>
</dependency>

2. 切面类定义

package com.jam.demo.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.Arrays;

/**
* 日志切面类,用于增强服务层方法的日志输出
* @author ken
*/

@Aspect
@Component
@Slf4j
public class LogAspect {
   /**
    * 切入点表达式:匹配com.jam.demo.service包下所有类的所有公共方法
    */

   @Pointcut("execution(public * com.jam.demo.service..*(..))")
   public void servicePointcut() {}
   
   /**
    * 前置通知:方法执行前执行
    * @param joinPoint 连接点对象,包含方法信息、参数等
    */

   @Before("servicePointcut()")
   public void beforeAdvice(JoinPoint joinPoint) {
       String methodName = joinPoint.getSignature().getName();
       String className = joinPoint.getTarget().getClass().getSimpleName();
       Object[] args = joinPoint.getArgs();
       log.info("【前置通知】{}类的{}方法开始执行,参数:{}", className, methodName, Arrays.toString(args));
   }
   
   /**
    * 后置通知:方法执行后执行(无论成功与否)
    * @param joinPoint 连接点对象
    */

   @After("servicePointcut()")
   public void afterAdvice(JoinPoint joinPoint) {
       String methodName = joinPoint.getSignature().getName();
       String className = joinPoint.getTarget().getClass().getSimpleName();
       log.info("【后置通知】{}类的{}方法执行结束", className, methodName);
   }
   
   /**
    * 返回通知:方法成功执行后执行,可获取返回值
    * @param joinPoint 连接点对象
    * @param result 方法返回值
    */

   @AfterReturning(pointcut = "servicePointcut()", returning = "result")
   public void afterReturningAdvice(JoinPoint joinPoint, Object result) {
       String methodName = joinPoint.getSignature().getName();
       String className = joinPoint.getTarget().getClass().getSimpleName();
       log.info("【返回通知】{}类的{}方法执行成功,返回值:{}", className, methodName, result);
   }
   
   /**
    * 异常通知:方法执行异常时执行
    * @param joinPoint 连接点对象
    * @param e 异常对象
    */

   @AfterThrowing(pointcut = "servicePointcut()", throwing = "e")
   public void afterThrowingAdvice(JoinPoint joinPoint, Exception e) {
       String methodName = joinPoint.getSignature().getName();
       String className = joinPoint.getTarget().getClass().getSimpleName();
       log.error("【异常通知】{}类的{}方法执行异常,异常信息:{}", className, methodName, e.getMessage(), e);
   }
   
   /**
    * 环绕通知:包围方法执行,可控制方法是否执行、执行时机
    * @param proceedingJoinPoint 可执行的连接点对象
    * @return 方法返回值
    * @throws Throwable 方法执行异常
    */

   @Around("servicePointcut()")
   public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
       String methodName = proceedingJoinPoint.getSignature().getName();
       String className = proceedingJoinPoint.getTarget().getClass().getSimpleName();
       long startTime = System.currentTimeMillis();
       
       Object result = null;
       try {
           // 执行前置逻辑
           log.info("【环绕通知-前置】{}类的{}方法开始计时", className, methodName);
           // 执行目标方法
           result = proceedingJoinPoint.proceed();
           // 执行返回后逻辑
           log.info("【环绕通知-返回】{}类的{}方法计时结束", className, methodName);
       } catch (Exception e) {
           // 执行异常逻辑
           log.error("【环绕通知-异常】{}类的{}方法执行异常", className, methodName, e);
           throw e;
       } finally {
           // 计算方法执行时间
           long costTime = System.currentTimeMillis() - startTime;
           log.info("【环绕通知-最终】{}类的{}方法执行耗时:{}ms", className, methodName, costTime);
       }
       return result;
   }
}

3. 服务类(被增强的目标对象)

package com.jam.demo.service;

import com.jam.demo.entity.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

/**
* 用户服务类,被AOP增强
* @author ken
*/

@Service
@Slf4j
public class UserService {
   /**
    * 根据用户ID查询用户
    * @param userId 用户ID
    * @return User对象
    */

   public User queryUserById(String userId) {
       // 模拟参数校验
       if (ObjectUtils.isEmpty(userId)) {
           throw new IllegalArgumentException("用户ID不能为空");
       }
       // 模拟数据库查询
       User user = new User();
       user.setUserId(userId);
       user.setUserName("张三");
       user.setAge(25);
       user.setPhone("13800138000");
       return user;
   }
   
   /**
    * 新增用户
    * @param user 用户对象
    * @return 新增成功的用户ID
    */

   public String addUser(User user) {
       // 模拟参数校验
       if (ObjectUtils.isEmpty(user) || !org.springframework.util.StringUtils.hasText(user.getUserName())) {
           throw new IllegalArgumentException("用户名不能为空");
       }
       // 模拟数据库新增
       String userId = "user_" + System.currentTimeMillis();
       user.setUserId(userId);
       log.info("用户新增成功,用户信息:{}", user);
       return userId;
   }
}

4. 实体类

package com.jam.demo.entity;

import lombok.Data;

/**
* 用户实体类
* @author ken
*/

@Data
public class User {
   /**
    * 用户ID
    */

   private String userId;
   /**
    * 用户名
    */

   private String userName;
   /**
    * 年龄
    */

   private Integer age;
   /**
    * 手机号
    */

   private String phone;
}

5. 配置类(开启AOP自动代理)

package com.jam.demo.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

/**
* 应用配置类,开启AOP自动代理并扫描组件
* @author ken
*/

@Configuration
@ComponentScan(basePackages = "com.jam.demo")
@EnableAspectJAutoProxy(proxyTargetClass = true) // proxyTargetClass=true:强制使用CGLIB代理
public class AopConfig {
}

6. 测试类

package com.jam.demo.aop;

import com.jam.demo.config.AopConfig;
import com.jam.demo.entity.User;
import com.jam.demo.service.UserService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
* AOP测试类
* @author ken
*/

public class AopTest {
   public static void main(String[] args) {
       // 初始化Spring上下文
       AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AopConfig.class);
       
       // 获取UserService代理对象
       UserService userService = context.getBean(UserService.class);
       
       // 测试正常方法调用
       System.out.println("===== 测试查询用户 =====");
       User user = userService.queryUserById("1001");
       
       // 测试新增用户
       System.out.println("\n===== 测试新增用户 =====");
       User newUser = new User();
       newUser.setUserName("李四");
       newUser.setAge(30);
       newUser.setPhone("13900139000");
       userService.addUser(newUser);
       
       // 测试异常情况
       System.out.println("\n===== 测试参数异常 =====");
       try {
           userService.queryUserById(null);
       } catch (Exception e) {
           // 异常已被切面捕获并日志输出
       }
       
       // 关闭上下文
       context.close();
   }
}

四、数据访问/集成模块:连接数据层

数据访问/集成模块提供了Spring对数据访问层的支持,简化了JDBC、ORM框架(MyBatis、Hibernate)、事务管理等操作。核心模块包括JDBC、ORM、Transactions等。

4.1 事务管理模块(Transactions)

Spring事务管理是该模块的核心功能,通过AOP实现声明式事务(注解@Transactional)或编程式事务,支持多种事务管理器(JDBC事务、JTA事务等)。

核心底层逻辑

  1. 事务管理器(PlatformTransactionManager):统一的事务管理接口,不同数据访问技术有不同实现(如DataSourceTransactionManager用于JDBC/MyBatis);
  2. 事务定义(TransactionDefinition):描述事务属性(隔离级别、传播行为、超时时间、是否只读);
  3. 事务状态(TransactionStatus):描述事务的当前状态(是否新建、是否已提交、是否回滚);
  4. 核心流程:通过AOP拦截标注@Transactional的方法,在方法执行前开启事务,执行成功后提交事务,执行异常时回滚事务。

实战示例:编程式事务(最新Spring 6.x)

1. 依赖配置(pom.xml)

<!-- Spring 事务核心依赖 -->
<dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-tx</artifactId>
   <version>6.1.5</version>
</dependency>
<!-- Spring JDBC -->
<dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-jdbc</artifactId>
   <version>6.1.5</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
   <groupId>com.mysql</groupId>
   <artifactId>mysql-connector-j</artifactId>
   <version>8.3.0</version>
</dependency>
<!-- MyBatis-Plus -->
<dependency>
   <groupId>com.baomidou</groupId>
   <artifactId>mybatis-plus-boot-starter</artifactId>
   <version>3.5.5</version>
</dependency>
<!-- HikariCP连接池(Spring默认) -->
<dependency>
   <groupId>com.zaxxer</groupId>
   <artifactId>HikariCP</artifactId>
   <version>5.1.0</version>
</dependency>
<!-- FastJSON2 -->
<dependency>
   <groupId>com.alibaba.fastjson2</groupId>
   <artifactId>fastjson2</artifactId>
   <version>2.0.45</version>
</dependency>
<!-- Google Guava 集合工具 -->
<dependency>
   <groupId>com.google.guava</groupId>
   <artifactId>guava</artifactId>
   <version>33.2.1-jre</version>
</dependency>
<!-- Swagger3 -->
<dependency>
   <groupId>io.springfox</groupId>
   <artifactId>springfox-boot-starter</artifactId>
   <version>3.0.0</version>
</dependency>

2. 数据库表SQL(MySQL 8.0)

-- 用户表
CREATE TABLE `t_user` (
 `user_id` varchar(64) NOT NULL COMMENT '用户ID',
 `user_name` varchar(32) NOT NULL COMMENT '用户名',
 `age` int DEFAULT NULL COMMENT '年龄',
 `phone` varchar(20) DEFAULT NULL COMMENT '手机号',
 `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
 PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';

-- 订单表
CREATE TABLE `t_order` (
 `order_id` varchar(64) NOT NULL COMMENT '订单ID',
 `user_id` varchar(64) NOT NULL COMMENT '用户ID',
 `order_amount` decimal(10,2) NOT NULL COMMENT '订单金额',
 `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
 PRIMARY KEY (`order_id`),
 KEY `idx_user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';

3. 实体类

package com.jam.demo.entity;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

import java.math.BigDecimal;
import java.time.LocalDateTime;

/**
* 订单实体类
* @author ken
*/

@Data
@TableName("t_order")
public class Order {
   /**
    * 订单ID
    */

   @TableId("order_id")
   private String orderId;
   
   /**
    * 用户ID
    */

   @TableField("user_id")
   private String userId;
   
   /**
    * 订单金额
    */

   @TableField("order_amount")
   private BigDecimal orderAmount;
   
   /**
    * 创建时间
    */

   @TableField("create_time")
   private LocalDateTime createTime;
}

4. Mapper接口(MyBatis-Plus)

package com.jam.demo.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.jam.demo.entity.Order;
import com.jam.demo.entity.User;
import org.springframework.stereotype.Repository;

/**
* 订单Mapper
* @author ken
*/

@Repository
public interface OrderMapper extends BaseMapper<Order> {
}

package com.jam.demo.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.jam.demo.entity.User;
import org.springframework.stereotype.Repository;

/**
* 用户Mapper
* @author ken
*/

@Repository
public interface UserMapper extends BaseMapper<User> {
}

5. 服务类(编程式事务)

package com.jam.demo.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.jam.demo.entity.Order;
import com.jam.demo.entity.User;
import com.jam.demo.mapper.OrderMapper;
import com.jam.demo.mapper.UserMapper;
import com.google.common.collect.Lists;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID;

/**
* 订单服务类(编程式事务)
* @author ken
*/

@Service
@Slf4j
@RequiredArgsConstructor
public class OrderService {
   private final OrderMapper orderMapper;
   private final UserMapper userMapper;
   private final PlatformTransactionManager transactionManager;
   
   /**
    * 创建订单(包含用户创建,编程式事务)
    * @param userName 用户名
    * @param age 年龄
    * @param phone 手机号
    * @param orderAmount 订单金额
    * @return 订单ID
    */

   public String createOrderWithUser(String userName, Integer age, String phone, BigDecimal orderAmount) {
       // 1. 参数校验
       if (!StringUtils.hasText(userName, "用户名不能为空")) {
           throw new IllegalArgumentException("用户名不能为空");
       }
       if (ObjectUtils.isEmpty(orderAmount) || orderAmount.compareTo(BigDecimal.ZERO) <= 0) {
           throw new IllegalArgumentException("订单金额必须大于0");
       }
       
       // 2. 定义事务属性:默认隔离级别、传播行为REQUIRED、非只读
       DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
       transactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
       transactionDefinition.setIsolationLevel(TransactionDefinition.ISOLATION_DEFAULT);
       transactionDefinition.setTimeout(30); // 超时时间30秒
       
       // 3. 开启事务
       TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition);
       
       try {
           // 3.1 创建用户
           User user = new User();
           String userId = "user_" + UUID.randomUUID().toString().replace("-", "");
           user.setUserId(userId);
           user.setUserName(userName);
           user.setAge(age);
           user.setPhone(phone);
           int userInsertCount = userMapper.insert(user);
           log.info("创建用户,影响行数:{},用户ID:{}", userInsertCount, userId);
           
           // 模拟异常(测试事务回滚)
           // if (true) {
           //     throw new RuntimeException("模拟创建订单异常");
           // }
           
           // 3.2 创建订单
           Order order = new Order();
           String orderId = "order_" + UUID.randomUUID().toString().replace("-", "");
           order.setOrderId(orderId);
           order.setUserId(userId);
           order.setOrderAmount(orderAmount);
           order.setCreateTime(LocalDateTime.now());
           int orderInsertCount = orderMapper.insert(order);
           log.info("创建订单,影响行数:{},订单ID:{}", orderInsertCount, orderId);
           
           // 4. 提交事务
           transactionManager.commit(transactionStatus);
           log.info("创建订单和用户成功,事务提交");
           return orderId;
       } catch (Exception e) {
           // 5. 回滚事务
           transactionManager.rollback(transactionStatus);
           log.error("创建订单和用户失败,事务回滚", e);
           throw new RuntimeException("创建订单失败:" + e.getMessage());
       }
   }
   
   /**
    * 根据用户ID查询订单列表
    * @param userId 用户ID
    * @return 订单列表
    */

   public List<Order> queryOrdersByUserId(String userId) {
       if (!StringUtils.hasText(userId, "用户ID不能为空")) {
           throw new IllegalArgumentException("用户ID不能为空");
       }
       
       LambdaQueryWrapper<Order> queryWrapper = new LambdaQueryWrapper<>();
       queryWrapper.eq(Order::getUserId, userId);
       List<Order> orderList = orderMapper.selectList(queryWrapper);
       
       if (CollectionUtils.isEmpty(orderList)) {
           log.info("用户ID:{} 暂无订单", userId);
           return Lists.newArrayList();
       }
       return orderList;
   }
}

6. 配置类(数据源、事务管理器、MyBatis-Plus)

package com.jam.demo.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import com.zaxxer.hikari.HikariDataSource;

import javax.sql.DataSource;
import java.util.Properties;

/**
* 数据访问层配置(数据源、事务管理器、MyBatis-Plus)
* @author ken
*/

@Configuration
@MapperScan(basePackages = "com.jam.demo.mapper") // 扫描Mapper接口
public class DataSourceConfig {
   /**
    * 配置HikariCP数据源(Spring 6.x默认)
    * @return 数据源
    */

   @Bean
   public DataSource dataSource() {
       HikariDataSource dataSource = new HikariDataSource();
       dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/spring_demo?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true");
       dataSource.setUsername("root");
       dataSource.setPassword("root123456");
       dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
       // 连接池配置
       dataSource.setMaximumPoolSize(10); // 最大连接数
       dataSource.setMinimumIdle(5); // 最小空闲连接数
       dataSource.setIdleTimeout(300000); // 空闲连接超时时间(5分钟)
       dataSource.setConnectionTimeout(30000); // 连接超时时间(30秒)
       return dataSource;
   }
   
   /**
    * 配置事务管理器(JDBC事务)
    * @param dataSource 数据源
    * @return 事务管理器
    */

   @Bean
   public PlatformTransactionManager transactionManager(DataSource dataSource) {
       return new DataSourceTransactionManager(dataSource);
   }
   
   /**
    * 配置MyBatis-Plus SqlSessionFactory
    * @param dataSource 数据源
    * @return SqlSessionFactory
    * @throws Exception 异常
    */

   @Bean
   public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
       MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean();
       sessionFactory.setDataSource(dataSource);
       
       // 配置MyBatis-Plus插件(分页插件)
       MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
       interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
       sessionFactory.setPlugins(interceptor);
       
       // 配置MyBatis属性
       Properties properties = new Properties();
       properties.setProperty("mapUnderscoreToCamelCase", "true"); // 下划线转驼峰
       properties.setProperty("logImpl", "SLF4J"); // 日志实现
       sessionFactory.setConfigurationProperties(properties);
       
       return sessionFactory.getObject();
   }
}

7. 控制器(结合Swagger3)

package com.jam.demo.controller;

import com.jam.demo.entity.Order;
import com.jam.demo.service.OrderService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;
import java.util.List;

/**
* 订单控制器
* @author ken
*/

@RestController
@RequestMapping("/api/order")
@Api(tags = "订单管理接口")
@RequiredArgsConstructor
@Slf4j
public class OrderController {
   private final OrderService orderService;
   
   /**
    * 创建订单(包含用户创建)
    * @param userName 用户名
    * @param age 年龄
    * @param phone 手机号
    * @param orderAmount 订单金额
    * @return 订单ID
    */

   @PostMapping("/create")
   @ApiOperation(value = "创建订单", notes = "创建订单并自动创建关联用户,包含编程式事务")
   public String createOrder(
           @ApiParam(value = "用户名", required = true)
@RequestParam String userName,
           @ApiParam(value = "年龄") @RequestParam(required = false) Integer age,
           @ApiParam(value = "手机号") @RequestParam(required = false) String phone,
           @ApiParam(value = "订单金额", required = true) @RequestParam BigDecimal orderAmount
   ) {
       return orderService.createOrderWithUser(userName, age, phone, orderAmount);
   }
   
   /**
    * 根据用户ID查询订单列表
    * @param userId 用户ID
    * @return 订单列表
    */

   @GetMapping("/list")
   @ApiOperation(value = "查询订单列表", notes = "根据用户ID查询关联的订单列表")
   public List<Order> queryOrderList(
           @ApiParam(value = "用户ID", required = true)
@RequestParam String userId
   ) {
       return orderService.queryOrdersByUserId(userId);
   }
}

8. 启动类

package com.jam.demo;

import io.swagger.annotations.Api;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import springfox.documentation.oas.annotations.EnableOpenApi;

/**
* 应用启动类
* @author ken
*/

@SpringBootApplication
@EnableOpenApi // 开启Swagger3
public class SpringDemoApplication {
   public static void main(String[] args) {
       SpringApplication.run(SpringDemoApplication.class, args);
       System.out.println("Spring Demo应用启动成功!访问Swagger3文档:http://localhost:8080/swagger-ui/index.html");
   }
}

4.2 ORM模块:整合MyBatis-Plus

Spring的ORM模块提供了对主流ORM框架的整合支持,通过Spring的事务管理和资源管理,简化ORM框架的使用。上面的实战示例已结合MyBatis-Plus实现了ORM操作,核心优势:

  • 统一的事务管理:MyBatis-Plus的操作自动纳入Spring事务;
  • 资源统一管理:数据源由Spring统一配置和管理,避免重复配置;
  • 简化依赖注入:Mapper接口由Spring自动扫描并注入到Service中。

五、Web模块:Spring的Web开发支持

Web模块提供了Spring对Web开发的全面支持,包含Web、WebMVC、WebFlux等子模块,分别对应传统的Servlet开发、MVC开发和响应式开发。

5.1 WebMVC模块:Spring MVC核心

Spring MVC是基于Servlet的MVC框架,实现了请求的接收、处理和响应的完整流程。核心组件包括:

  • 前端控制器(DispatcherServlet):统一接收请求,分发到对应的处理器;
  • 处理器映射器(HandlerMapping):根据请求路径查找对应的处理器(Controller方法);
  • 处理器适配器(HandlerAdapter):调用处理器方法,执行业务逻辑;
  • 视图解析器(ViewResolver):将处理器返回的结果解析为视图(JSP、JSON等);
  • 拦截器(HandlerInterceptor):拦截请求,执行前置、后置处理(如登录校验)。

Spring MVC请求处理流程

image.png

实战示例:Spring MVC拦截器(登录校验)

1. 拦截器实现类

package com.jam.demo.interceptor;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.util.StringUtils;

/**
* 登录校验拦截器
* @author ken
*/

@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
   /**
    * 前置处理:请求执行前拦截
    * @param request 请求对象
    * @param response 响应对象
    * @param handler 处理器
    * @return true:放行;false:拦截
    * @throws Exception 异常
    */

   @Override
   public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
       // 1. 获取请求路径
       String requestURI = request.getRequestURI();
       log.info("登录拦截器拦截请求:{}", requestURI);
       
       // 2. 放行登录接口
       if (requestURI.contains("/api/login")) {
           log.info("登录接口,直接放行");
           return true;
       }
       
       // 3. 校验登录状态(从Session获取用户信息)
       HttpSession session = request.getSession();
       Object user = session.getAttribute("loginUser");
       if (!StringUtils.isEmpty(user)) {
           log.info("用户已登录,放行请求");
           return true;
       }
       
       // 4. 未登录,返回401未授权
       log.info("用户未登录,拦截请求");
       response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
       response.setContentType("application/json;charset=utf-8");
       response.getWriter().write("{\"code\":401,\"msg\":\"未登录,请先登录\"}");
       return false;
   }
   
   /**
    * 后置处理:处理器方法执行后,视图渲染前
    * @param request 请求对象
    * @param response 响应对象
    * @param handler 处理器
    * @param modelAndView 模型和视图
    * @throws Exception 异常
    */

   @Override
   public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
       log.info("登录拦截器后置处理");
   }
   
   /**
    * 完成处理:视图渲染后,请求完成
    * @param request 请求对象
    * @param response 响应对象
    * @param handler 处理器
    * @param ex 异常对象
    * @throws Exception 异常
    */

   @Override
   public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
       log.info("登录拦截器完成处理");
   }
}

2. 拦截器配置类

package com.jam.demo.config;

import com.jam.demo.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
* Web MVC配置类,注册拦截器
* @author ken
*/

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
   @Override
   public void addInterceptors(InterceptorRegistry registry) {
       // 注册登录拦截器,拦截所有/api/**路径的请求
       registry.addInterceptor(new LoginInterceptor())
               .addPathPatterns("/api/**")
               .excludePathPatterns("/api/login"); // 放行登录接口
   }
}

3. 登录控制器

package com.jam.demo.controller;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpSession;

/**
* 登录控制器
* @author ken
*/

@RestController
@RequestMapping("/api")
@Api(tags = "登录管理接口")
@Slf4j
public class LoginController {
   /**
    * 登录接口
    * @param username 用户名
    * @param password 密码
    * @param session Session对象
    * @return 登录结果
    */

   @PostMapping("/login")
   @ApiOperation(value = "用户登录", notes = "用户登录并将用户信息存入Session")
   public String login(
           @ApiParam(value = "用户名", required = true)
@RequestParam String username,
           @ApiParam(value = "密码", required = true) @RequestParam String password,
           HttpSession session
   ) {
       // 简单校验(实际开发中需查询数据库)
       if (!StringUtils.hasText(username, "用户名不能为空") || !StringUtils.hasText(password, "密码不能为空")) {
           return "{\"code\":400,\"msg\":\"用户名或密码不能为空\"}";
       }
       if ("admin".equals(username) && "admin123".equals(password)) {
           // 登录成功,存入Session
           session.setAttribute("loginUser", username);
           log.info("用户{}登录成功", username);
           return "{\"code\":200,\"msg\":\"登录成功\"}";
       }
       log.info("用户{}登录失败,用户名或密码错误", username);
       return "{\"code\":401,\"msg\":\"用户名或密码错误\"}";
   }
}

六、易混淆技术点辨析

6.1 BeanFactory vs ApplicationContext

特性 BeanFactory ApplicationContext
加载方式 懒加载(默认),获取Bean时才创建 非懒加载(默认),启动时创建单例Bean
功能范围 核心Bean管理功能 包含BeanFactory所有功能,新增国际化、事件发布、资源访问等
适用场景 资源紧张的环境(如移动端) 企业级应用(绝大多数场景)
实现类 DefaultListableBeanFactory AnnotationConfigApplicationContext、ClassPathXmlApplicationContext等

6.2 JDK 动态代理 vs CGLIB 动态代理

特性 JDK 动态代理 CGLIB 动态代理
实现原理 基于接口,生成接口的代理类 基于继承,生成目标类的子类
目标对象要求 必须实现接口 无需实现接口(但不能是final类/方法)
创建效率 较快(直接生成接口代理类字节码) 较慢(需通过ASM框架修改字节码)
执行效率 较慢(反射调用目标方法) 较快(直接调用子类方法,无反射)
内存占用 较少 较多(生成的子类字节码更复杂)
Spring 中的使用场景 目标对象实现接口时默认使用 目标对象无接口时自动使用;可通过proxyTargetClass=true强制使用

核心差异总结

  1. 底层依赖不同:JDK动态代理依赖JDK原生的java.lang.reflect.ProxyInvocationHandler,CGLIB依赖ASM字节码操作框架;
  2. 适用场景不同:JDK动态代理仅支持接口实现类,CGLIB支持任意非final类;
  3. 性能特点不同:JDK动态代理创建快、执行慢,CGLIB创建慢、执行快(适合单例Bean的长期使用场景)。

实战示例:JDK 动态代理 vs CGLIB 动态代理对比

1. JDK 动态代理实现(基于接口)

package com.jam.demo.proxy;

import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ObjectUtils;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
* JDK 动态代理示例(基于接口)
* @author ken
*/

@Slf4j
public class JdkProxyDemo {
   // 目标接口
   public interface UserDao {
       /**
        * 保存用户
        * @param userName 用户名
        * @return 保存结果
        */

       boolean saveUser(String userName);
   }

   // 目标对象(实现接口)
   public static class UserDaoImpl implements UserDao {
       @Override
       public boolean saveUser(String userName) {
           if (!org.springframework.util.StringUtils.hasText(userName)) {
               log.error("用户名不能为空");
               return false;
           }
           log.info("JDK动态代理-保存用户:{}", userName);
           return true;
       }
   }

   // JDK动态代理处理器
   public static class JdkProxyHandler implements InvocationHandler {
       // 目标对象
       private final Object target;

       public JdkProxyHandler(Object target) {
           this.target = target;
       }

       /**
        * 代理方法执行逻辑
        * @param proxy 代理对象
        * @param method 目标方法
        * @param args 方法参数
        * @return 方法返回值
        * @throws Throwable 异常
        */

       @Override
       public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
           // 前置增强:日志输出
           log.info("【JDK代理-前置】执行方法:{},参数:{}", method.getName(), args);
           
           // 执行目标方法
           Object result = method.invoke(target, args);
           
           // 后置增强:结果输出
           log.info("【JDK代理-后置】方法执行完成,返回值:{}", result);
           return result;
       }
   }

   // 测试方法
   public static void main(String[] args) {
       // 1. 创建目标对象
       UserDao target = new UserDaoImpl();
       
       // 2. 创建JDK动态代理对象
       UserDao proxy = (UserDao) Proxy.newProxyInstance(
               target.getClass().getClassLoader(),
               target.getClass().getInterfaces(),
               new JdkProxyHandler(target)
       );
       
       // 3. 调用代理方法
       proxy.saveUser("张三");
       
       // 验证代理对象类型
       log.info("代理对象类型:{}", proxy.getClass().getName());
       log.info("是否为目标对象实例:{}", proxy instanceof UserDaoImpl);
   }
}

2. CGLIB 动态代理实现(基于继承)

package com.jam.demo.proxy;

import lombok.extern.slf4j.Slf4j;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;

/**
* CGLIB 动态代理示例(基于继承)
* @author ken
*/

@Slf4j
public class CglibProxyDemo {
   // 目标对象(无需实现接口)
   public static class OrderDao {
       /**
        * 创建订单
        * @param orderId 订单ID
        * @return 创建结果
        */

       public boolean createOrder(String orderId) {
           if (!StringUtils.hasText(orderId)) {
               log.error("订单ID不能为空");
               return false;
           }
           log.info("CGLIB动态代理-创建订单:{}", orderId);
           return true;
       }
   }

   // CGLIB方法拦截器
   public static class CglibProxyInterceptor implements MethodInterceptor {
       /**
        * 拦截目标方法,执行增强逻辑
        * @param obj 代理对象(目标类的子类)
        * @param method 目标方法
        * @param args 方法参数
        * @param proxy 方法代理对象(用于调用目标方法)
        * @return 方法返回值
        * @throws Throwable 异常
        */

       @Override
       public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
           // 前置增强:日志输出
           log.info("【CGLIB代理-前置】执行方法:{},参数:{}", method.getName(), args);
           
           // 执行目标方法(使用proxy.invokeSuper避免递归调用)
           Object result = proxy.invokeSuper(obj, args);
           
           // 后置增强:结果输出
           log.info("【CGLIB代理-后置】方法执行完成,返回值:{}", result);
           return result;
       }
   }

   // 测试方法
   public static void main(String[] args) {
       // 1. 创建CGLIB增强器
       Enhancer enhancer = new Enhancer();
       // 2. 设置父类(目标类)
       enhancer.setSuperclass(OrderDao.class);
       // 3. 设置方法拦截器
       enhancer.setCallback(new CglibProxyInterceptor());
       
       // 4. 创建代理对象(目标类的子类)
       OrderDao proxy = (OrderDao) enhancer.create();
       
       // 5. 调用代理方法
       proxy.createOrder("order_1001");
       
       // 验证代理对象类型
       log.info("代理对象类型:{}", proxy.getClass().getName());
       log.info("是否为目标对象实例:{}", proxy instanceof OrderDao);
   }
}

3. 依赖配置(pom.xml,CGLIB 依赖)

<!-- CGLIB 依赖(Spring AOP已包含,单独使用时需引入) -->
<dependency>
   <groupId>cglib</groupId>
   <artifactId>cglib</artifactId>
   <version>3.3.0</version>
</dependency>

6.3 声明式事务 vs 编程式事务

特性 声明式事务(@Transactional) 编程式事务(PlatformTransactionManager)
实现方式 基于AOP,注解驱动 手动编码调用事务API
代码侵入性 低(仅需添加注解) 高(业务代码中嵌入事务逻辑)
灵活性 较低(依赖注解属性配置) 较高(可灵活控制事务边界、异常处理)
配置复杂度 低(注解+少量配置) 高(需手动管理事务开启、提交、回滚)
适用场景 简单事务场景(单方法、固定事务属性) 复杂事务场景(多方法组合、动态事务属性)
异常处理 默认仅回滚RuntimeException及其子类 可自定义回滚异常类型

核心差异总结

  1. 核心思想不同:声明式事务是“面向配置”,通过注解简化事务管理;编程式事务是“面向代码”,通过API精确控制事务流程;
  2. 代码侵入性不同:声明式事务几乎无侵入,符合“开闭原则”;编程式事务侵入业务代码,维护成本高;
  3. 适用场景不同:绝大多数简单业务用声明式事务;复杂业务(如多步骤事务控制、动态决定事务属性)用编程式事务。

实战示例:声明式事务快速实现

package com.jam.demo.transaction;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.jam.demo.entity.User;
import com.jam.demo.mapper.UserMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

/**
* 声明式事务示例
* @author ken
*/

@Service
@Slf4j
@RequiredArgsConstructor
public class DeclarativeTransactionService {
   private final UserMapper userMapper;

   /**
    * 声明式事务:修改用户信息
    * 事务属性:默认传播行为REQUIRED,默认回滚RuntimeException
    * @param userId 用户ID
    * @param newUserName 新用户名
    */

   @Transactional(rollbackFor = Exception.class) // 指定所有异常都回滚
   public void updateUserName(String userId, String newUserName)
{
       // 参数校验
       if (!StringUtils.hasText(userId, "用户ID不能为空") || !StringUtils.hasText(newUserName, "新用户名不能为空")) {
           throw new IllegalArgumentException("用户ID或新用户名不能为空");
       }

       // 1. 查询用户
       LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
       queryWrapper.eq(User::getUserId, userId);
       User user = userMapper.selectOne(queryWrapper);
       if (user == null) {
           throw new RuntimeException("用户不存在,用户ID:" + userId);
       }

       // 2. 修改用户名
       user.setUserName(newUserName);
       userMapper.updateById(user);
       log.info("修改用户{}的用户名为:{}", userId, newUserName);

       // 模拟异常(测试事务回滚)
       // if (true) {
       //     throw new RuntimeException("模拟修改用户信息异常");
       // }
   }
}

6.4 Spring AOP vs AspectJ

特性 Spring AOP AspectJ
实现方式 动态代理(JDK/CGLIB),运行时增强 字节码织入,编译期/类加载期增强
增强时机 运行时(程序运行时动态生成代理对象) 编译期(.java→.class时织入)/类加载期(.class加载到JVM时织入)
功能范围 仅支持方法级别的增强 支持方法、字段、构造函数等多种增强点
代码侵入性 低(无需修改目标类代码) 编译期织入无侵入,加载期织入需修改类加载器
性能 运行时增强有一定性能开销 编译期织入无运行时开销,性能更优
集成复杂度 低(Spring框架内置支持) 高(需引入AspectJ编译器、配置织入)
适用场景 绝大多数企业级应用的方法增强场景 需字段/构造函数增强、对性能要求极高的场景

核心差异总结

  1. 增强机制本质不同:Spring AOP是“运行时增强”,基于动态代理;AspectJ是“编译期/类加载期增强”,基于字节码织入;
  2. 功能完备性不同:AspectJ功能更强大,支持更多增强点;Spring AOP聚焦方法增强,满足大部分业务需求;
  3. 易用性不同:Spring AOP集成简单,无需额外编译工具;AspectJ需额外配置编译器(如ajc),使用成本高;
  4. 性能不同:AspectJ编译期织入无运行时开销,性能优于Spring AOP;Spring AOP运行时动态代理有轻微性能损耗,但可忽略不计。

七、Spring 体系结构核心总结

Spring 体系结构的核心是“分层设计+模块化拆分”,围绕“IOC容器”和“AOP”两大核心思想,构建了覆盖核心容器、数据访问、Web开发、测试等全链路的生态体系。

7.1 核心模块依赖关系

  1. 核心容器(Core Container)是基础,所有其他模块都依赖于它;
  2. AOP模块依赖核心容器,通过IOC容器管理切面和目标对象;
  3. 数据访问/集成模块依赖核心容器和AOP,通过AOP实现事务管理;
  4. Web模块依赖核心容器和AOP,通过IOC管理Controller、Service等Bean,通过AOP实现拦截器等功能;
  5. 测试模块依赖核心容器,提供对Spring Bean的测试支持。

7.2 核心设计思想落地

  1. IOC容器:将对象的创建、依赖管理交给Spring,实现“组件解耦”,核心载体是BeanFactory和ApplicationContext;
  2. AOP:通过“关注点分离”,将通用功能(日志、事务、权限)从业务代码中抽离,通过动态代理/字节码增强织入业务流程,实现“代码复用”。

7.3 实践选型建议

  1. 容器选择:优先使用ApplicationContext(AnnotationConfigApplicationContext/AnnotationConfigServletWebServerApplicationContext),注解驱动开发更高效;
  2. 代理选择:默认使用Spring AOP的动态代理,无需手动干预;若目标对象无接口且需增强final方法,可考虑AspectJ;
  3. 事务选择:简单业务用声明式事务(@Transactional),复杂业务用编程式事务;
  4. 数据访问:整合MyBatis-Plus简化ORM操作,使用HikariCP连接池提升性能;
  5. Web开发:Spring MVC用于传统Servlet开发,Spring WebFlux用于响应式开发(高并发场景)。

八、附录:实战项目依赖汇总(pom.xml完整配置)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

   <modelVersion>4.0.0</modelVersion>

   <parent>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-parent</artifactId>
       <version>3.2.4</version> <!-- Spring Boot最新稳定版本,对应Spring 6.1.5 -->
       <relativePath/>
   </parent>

   <groupId>com.jam.demo</groupId>
   <artifactId>spring-system-demo</artifactId>
   <version>1.0.0</version>
   <name>spring-system-demo</name>
   <description>Spring体系结构实战演示项目</description>

   <properties>
       <java.version>17</java.version>
       <lombok.version>1.18.30</lombok.version>
       <mybatis-plus.version>3.5.5</mybatis-plus.version>
       <fastjson2.version>2.0.45</fastjson2.version>
       <guava.version>33.2.1-jre</guava.version>
       <swagger.version>3.0.0</swagger.version>
       <cglib.version>3.3.0</cglib.version>
   </properties>

   <dependencies>
       <!-- Spring Boot核心依赖 -->
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter</artifactId>
       </dependency>

       <!-- Spring Web依赖(Spring MVC) -->
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-web</artifactId>
       </dependency>

       <!-- Spring AOP依赖 -->
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-aop</artifactId>
       </dependency>

       <!-- Spring 事务依赖 -->
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-jdbc</artifactId>
       </dependency>

       <!-- Lombok -->
       <dependency>
           <groupId>org.projectlombok</groupId>
           <artifactId>lombok</artifactId>
           <version>${lombok.version}</version>
           <scope>provided</scope>
       </dependency>

       <!-- MyBatis-Plus -->
       <dependency>
           <groupId>com.baomidou</groupId>
           <artifactId>mybatis-plus-boot-starter</artifactId>
           <version>${mybatis-plus.version}</version>
       </dependency>

       <!-- MySQL驱动 -->
       <dependency>
           <groupId>com.mysql</groupId>
           <artifactId>mysql-connector-j</artifactId>
           <scope>runtime</scope>
       </dependency>

       <!-- FastJSON2 -->
       <dependency>
           <groupId>com.alibaba.fastjson2</groupId>
           <artifactId>fastjson2</artifactId>
           <version>${fastjson2.version}</version>
       </dependency>

       <!-- Google Guava -->
       <dependency>
           <groupId>com.google.guava</groupId>
           <artifactId>guava</artifactId>
           <version>${guava.version}</version>
       </dependency>

       <!-- Swagger3 -->
       <dependency>
           <groupId>io.springfox</groupId>
           <artifactId>springfox-boot-starter</artifactId>
           <version>${swagger.version}</version>
       </dependency>

       <!-- CGLIB -->
       <dependency>
           <groupId>cglib</groupId>
           <artifactId>cglib</artifactId>
           <version>${cglib.version}</version>
       </dependency>

       <!-- 测试依赖 -->
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-test</artifactId>
           <scope>test</scope>
       </dependency>
   </dependencies>

   <build>
       <plugins>
           <!-- Spring Boot Maven插件 -->
           <plugin>
               <groupId>org.springframework.boot</groupId>
               <artifactId>spring-boot-maven-plugin</artifactId>
               <configuration>
                   <excludes>
                       <exclude>
                           <groupId>org.projectlombok</groupId>
                           <artifactId>lombok</artifactId>
                       </exclude>
                   </excludes>
               </configuration>
           </plugin>

           <!-- Java编译插件 -->
           <plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-compiler-plugin</artifactId>
               <version>3.8.1</version>
               <configuration>
                   <source>${java.version}</source>
                   <target>${java.version}</target>
                   <encoding>UTF-8</encoding>
               </configuration>
           </plugin>
       </plugins>
   </build>
</project>


总结

Spring 体系结构并非孤立的模块堆砌,而是以“IOC容器”为核心、以“AOP”为扩展手段,层层递进、相互支撑的完整生态。理解Spring体系结构,关键在于抓住“IOC解耦”和“AOP复用”两大核心思想,明确各模块的定位和依赖关系。

目录
相关文章
|
7天前
|
数据采集 人工智能 安全
|
16天前
|
云安全 监控 安全
|
2天前
|
机器学习/深度学习 人工智能 前端开发
构建AI智能体:七十、小树成林,聚沙成塔:随机森林与大模型的协同进化
随机森林是一种基于决策树的集成学习算法,通过构建多棵决策树并结合它们的预测结果来提高准确性和稳定性。其核心思想包括两个随机性:Bootstrap采样(每棵树使用不同的训练子集)和特征随机选择(每棵树分裂时只考虑部分特征)。这种方法能有效处理大规模高维数据,避免过拟合,并评估特征重要性。随机森林的超参数如树的数量、最大深度等可通过网格搜索优化。该算法兼具强大预测能力和工程化优势,是机器学习中的常用基础模型。
266 155
|
3天前
|
机器学习/深度学习 人工智能 前端开发
构建AI智能体:六十九、Bootstrap采样在大模型评估中的应用:从置信区间到模型稳定性
Bootstrap采样是一种通过有放回重抽样来评估模型性能的统计方法。它通过从原始数据集中随机抽取样本形成多个Bootstrap数据集,计算统计量(如均值、标准差)的分布,适用于小样本和非参数场景。该方法能估计标准误、构建置信区间,并量化模型不确定性,但对计算资源要求较高。Bootstrap特别适合评估大模型的泛化能力和稳定性,在集成学习、假设检验等领域也有广泛应用。与传统方法相比,Bootstrap不依赖分布假设,在非正态数据中表现更稳健。
206 105
|
10天前
|
SQL 自然语言处理 调度
Agent Skills 的一次工程实践
**本文采用 Agent Skills 实现整体智能体**,开发框架采用 AgentScope,模型使用 **qwen3-max**。Agent Skills 是 Anthropic 新推出的一种有别于mcp server的一种开发方式,用于为 AI **引入可共享的专业技能**。经验封装到**可发现、可复用的能力单元**中,每个技能以文件夹形式存在,包含特定任务的指导性说明(SKILL.md 文件)、脚本代码和资源等 。大模型可以根据需要动态加载这些技能,从而扩展自身的功能。目前不少国内外的一些框架也开始支持此种的开发方式,详细介绍如下。
723 5
|
13天前
|
人工智能 自然语言处理 API
一句话生成拓扑图!AI+Draw.io 封神开源组合,工具让你的效率爆炸
一句话生成拓扑图!next-ai-draw-io 结合 AI 与 Draw.io,通过自然语言秒出架构图,支持私有部署、免费大模型接口,彻底解放生产力,绘图效率直接爆炸。
813 153