引言
在Spring框架中,AOP(面向切面编程)和事务管理是两大核心功能。AOP通过代理模式实现方法增强,事务管理则确保数据操作的原子性。本文将结合核心概念、工作流程和实际案例,全面解析这两项技术。
一、AOP核心概念与工作流程
(一)AOP的核心概念
概念:AOP(Aspect Oriented Programming)面向切面编程,一种编程范式
作用:在不惊动原始设计的基础上为方法进行功能增强
核心概念
代理(Proxy):SpringAOP的核心本质是采用代理模式实现的
连接点(JoinPoint):在SpringAOP中,理解为任意方法的执行
切入点(Pointcut):匹配连接点的式子,也是具有共性功能的方法描述
通知(Advice):若干个方法的共性功能,在切入点处执行,最终体现为一个方法
切面(Aspect):描述通知与切入点的对应关系
目标对象(Target):被代理的原始对象成为目标对象
(二)动态代理:AOP的基石
AOP底层基于动态代理实现,分为两种模式:
JDK动态代理:通过接口实现,生成
com.sun.proxy.$Proxy0代理类CGLIB代理:针对无接口的类,通过子类继承实现
代理模式的组成:
抽象主题(接口)
真实主题(实现类)
代理类(增强逻辑的载体)
// JDK动态代理示例 public class JdkProxy implements InvocationHandler { private Object target; // 目标对象 public Object invoke(Object proxy, Method method, Object[] args) { System.out.println("前置增强"); Object result = method.invoke(target, args); System.out.println("后置增强"); return result; } }(三) AOP工作流程
Spring容器启动时的AOP流程:
加载Bean:初始化目标对象和通知类
解析切入点:读取所有
@Pointcut配置创建代理对象:匹配切入点的方法生成代理
执行增强逻辑:调用代理对象方法时织入通知
Spring 容器进行管理 做bean(Service)的增强
a spring 容器启动
b 读取切入点(pointcut)
c 初始化bean (创建代理对象)
d 获取bean (已经增强的方法)
二、AOP通知类型与应用
(一)核心注解
| 注解 | 作用 | 使用位置 | 必要参数 | 注意事项 |
|---|---|---|---|---|
@EnableAspectJAutoProxy | 启用Spring AOP代理机制 | 配置类上方 | proxyTargetClassexposeProxy | 必须添加才能启用AOP |
@Aspect | 声明类为切面容器 | 切面类上方 | - | 需配合@Component使用 |
@Pointcut | 定义方法切入点 | 空方法上方 | 表达式字符串 | 方法体必须为空 |
@EnableAspectJAutoProxy配置类启用AOP
@Configuration @EnableAspectJAutoProxy( // 核心启用注解 proxyTargetClass = true, // 强制使用CGLIB代理 exposeProxy = true // 暴露代理对象(AopContext.currentProxy()) ) @ComponentScan("com.example.service") public class AppConfig { // 配置类其他内容... }@Aspect+@Component- 声明切面类
@Aspect // 声明为切面类 @Component // 纳入Spring容器管理 public class SecurityAspect { // 切入点定义和通知方法将写在此类中 }@Pointcut- 定义切入点表达式
@Aspect @Component public class LoggingAspect { // 定义service包下所有方法的切入点 @Pointcut("execution(* com.example.service.*.*(..))") public void serviceMethods() {} // 方法体必须为空! // 定义带@Audit注解的方法切入点 @Pointcut("@annotation(com.example.Audit)") public void auditedMethods() {} // 组合切入点:匹配service包中带@Audit注解的方法 @Pointcut("serviceMethods() && auditedMethods()") public void auditedServiceMethods() {} }(二)五种通知类型
| 通知类型 | 注解 | 执行时机 |
|---|---|---|
| 前置通知 | @Before | 方法执行前 |
| 后置通知 | @After | 方法执行后(无论是否异常) |
| 环绕通知 | @Around | 方法执行前后(需调用proceed()) |
| 返回后通知 | @AfterReturning | 方法正常返回后 |
| 异常通知 | @AfterThrowing | 方法抛出异常后 |
@Around("execution(* com.example.*.*(..))") public Object around(ProceedingJoinPoint pjp) throws Throwable { long start = System.currentTimeMillis(); Object result = pjp.proceed(); // 调用原始方法 long time = System.currentTimeMillis() - start; System.out.println("方法执行耗时:" + time + "ms"); return result; }三、AOP切入点
切入点表达式是AOP的核心能力,它精确控制哪些方法需要被增强。掌握其语法和优化技巧是高效使用AOP的关键。
切入点表达式标准格式:动作关键字(访问修饰符 返回值 包名.类/接口名.方法名(参数)异常
名)
execution(* com.mytest.service.*Service.*(..))| 场景 | 推荐表达式 | 优势 |
|---|---|---|
| 业务层方法匹配 | execution(* com..service.*Service.*(..)) | 精确匹配Service结尾的类 |
| 查询方法匹配 | execution(public java.util.List+ find*(..)) | 明确返回集合类型 |
| 注解驱动切面 | @annotation(com.example.Audit) | 解耦业务逻辑 |
| 参数化切入点 | args(username) && @annotation(audit) | 同时获取参数和注解 |
避免
within(com.example..*)全包扫描(性能杀手)慎用
*作为方法名首字符(如*Dao会匹配deleteDao)使用
+匹配子类时注意继承深度
四、Spring事务管理
(一)事务核心概念
PlatformTransactionManager:事务管理器接口
@Transactional:声明式事务注解
事务传播行为:控制事务边界(如
REQUIRED/REQUIRES_NEW)
| 注解 | 类型 | 作用位置 | 关键属性 | 使用场景说明 |
|---|---|---|---|---|
@EnableTransactionManagement | 配置类注解 | 配置类上方 | - | Spring事务的总开关,无此注解事务管理不生效 |
@Transactional | 方法/类/接口注解 | 业务层方法/类/接口 | propagationisolationtimeoutrollbackFornoRollbackFor | 声明事务属性,标记方法需要事务管理 |
@EnableTransactionManagement- 配置类启用事务
@Configuration @EnableTransactionManagement // 核心开关 @PropertySource("classpath:jdbc.properties") @ComponentScan("com.example.service") public class AppConfig { @Bean public PlatformTransactionManager transactionManager(DataSource dataSource) { // 必须配置事务管理器 return new DataSourceTransactionManager(dataSource); } }@Transactional- 声明式事务控制
@Service // 类级事务配置(所有方法默认REQUIRED传播行为) @Transactional(isolation = Isolation.READ_COMMITTED, timeout = 30) public class OrderService { // 方法级配置(覆盖类级设置) @Transactional( propagation = Propagation.REQUIRES_NEW, rollbackFor = PaymentException.class, noRollbackFor = ValidationException.class ) public void createOrder(Order order) { // 数据库操作1:库存扣减 inventoryService.deduct(order); // 数据库操作2:订单创建 orderDao.save(order); // 数据库操作3:支付记录 paymentService.record(order); } }事务传播行为对照表
| 传播行为 | 值 | 说明 |
|---|---|---|
| REQUIRED | 默认值 | 当前有事务则加入,无则新建(推荐写操作使用) |
| REQUIRES_NEW | 总是新建事务 | 挂起当前事务并创建新事务(适用于日志记录等独立操作) |
| SUPPORTS | 支持当前事务 | 有事务则加入,无则以非事务方式执行(适合查询方法) |
| NOT_SUPPORTED | 非事务执行 | 挂起当前事务,以非事务方式执行 |
| MANDATORY | 强制有事务 | 必须在事务中调用,否则抛出异常 |
| NEVER | 禁止事务 | 必须在非事务环境下调用,否则抛出异常 |
| NESTED | 嵌套事务 | 当前有事务时创建保存点,可部分回滚(需JDBC 3.0驱动支持) |
(二)转账案例中的事务应用
示例
@Configuration @EnableTransactionManagement // 开启事务管理 public class AppConfig { @Bean public PlatformTransactionManager transactionManager(DataSource ds) { return new DataSourceTransactionManager(ds); // JDBC事务管理器 } } @Service public class AccountService { @Transactional( rollbackFor = Exception.class, propagation = Propagation.REQUIRED ) public void transfer(String from, String to, double amount) { accountDao.deduct(from, amount); accountDao.add(to, amount); } }(三) 事务传播行为解决日志记录问题
场景:转账无论成功与否都需记录日志
@Service public class LogService { // 始终开启新事务 @Transactional(propagation = Propagation.REQUIRES_NEW) public void logTransfer(String from, String to, double amount) { logDao.save("转账:" + from + "→" + to + " 金额:" + amount); } } // 在转账方法中调用 public void transfer(String from, String to, double amount) { try { accountService.transfer(from, to, amount); } finally { logService.logTransfer(from, to, amount); // 独立事务记录日志 } }