您好,登录后才能下订单哦!
# Spring AOP的介绍和应用
## 目录
1. [AOP基本概念](#1-aop基本概念)
- 1.1 [什么是AOP](#11-什么是aop)
- 1.2 [AOP核心术语](#12-aop核心术语)
2. [Spring AOP架构](#2-spring-aop架构)
- 2.1 [代理机制](#21-代理机制)
- 2.2 [织入时机](#22-织入时机)
3. [核心注解详解](#3-核心注解详解)
- 3.1 [@Aspect](#31-aspect)
- 3.2 [五种通知类型](#32-五种通知类型)
4. [实际应用场景](#4-实际应用场景)
- 4.1 [日志记录](#41-日志记录)
- 4.2 [事务管理](#42-事务管理)
5. [性能优化建议](#5-性能优化建议)
6. [总结](#6-总结)
## 1. AOP基本概念
### 1.1 什么是AOP
面向切面编程(Aspect-Oriented Programming)是一种通过预编译方式和运行期动态代理实现程序功能统一维护的技术。与OOP的纵向继承体系不同,AOP采用横向抽取机制,将分散在各个方法中的重复代码提取到切面中。
```java
// 传统OOP方式
class UserService {
public void createUser() {
// 业务逻辑
log.info("操作日志"); // 重复代码
}
public void deleteUser() {
// 业务逻辑
log.info("操作日志"); // 重复代码
}
}
// AOP方式
@Aspect
class LogAspect {
@After("execution(* com.service.*.*(..))")
public void writeLog() {
log.info("操作日志");
}
}
术语 | 说明 |
---|---|
Aspect(切面) | 封装横切逻辑的模块,包含Pointcut和Advice |
JoinPoint | 程序执行过程中的特定点,如方法调用、异常抛出等 |
Pointcut | 匹配JoinPoint的谓词,使用表达式定义拦截范围 |
Advice | 切面在特定JoinPoint执行的动作,包括Around、Before、After等类型 |
Target Object | 被代理的目标对象 |
AOP Proxy | 由AOP框架创建的代理对象,实现切面逻辑的织入 |
Weaving | 将切面应用到目标对象的过程,分为编译期、类加载期和运行期三种织入方式 |
Spring AOP默认使用JDK动态代理(要求目标类实现接口),当目标类未实现接口时自动切换为CGLIB代理:
// JDK动态代理示例
public class JdkProxyDemo {
interface Service {
void doSomething();
}
static class Target implements Service {
public void doSomething() {
System.out.println("业务方法执行");
}
}
public static void main(String[] args) {
Service proxy = (Service) Proxy.newProxyInstance(
JdkProxyDemo.class.getClassLoader(),
new Class[]{Service.class},
(p, method, params) -> {
System.out.println("前置处理");
return method.invoke(new Target(), params);
});
proxy.doSomething();
}
}
Spring AOP采用运行时织入,通过BeanPostProcessor在IoC容器初始化阶段完成代理对象的创建:
@startuml
participant "IoC容器" as container
participant "BeanPostProcessor" as processor
participant "目标Bean" as bean
participant "AOP代理" as proxy
container -> processor: 初始化Bean
processor -> bean: 实例化原始对象
processor -> proxy: 创建代理对象
container <- proxy: 返回代理Bean
@enduml
声明切面类的核心注解,需要与@Component配合使用:
@Aspect
@Component
public class SecurityAspect {
@Pointcut("@annotation(com.example.RequireAuth)")
public void authPointcut() {}
@Before("authPointcut()")
public void checkAuth() {
if(!SecurityContext.isAuthenticated()) {
throw new SecurityException("无权限访问");
}
}
}
@Before("execution(* com.service.*.*(..))")
public void beforeAdvice(JoinPoint jp) {
log.info("准备执行: " + jp.getSignature());
}
@AfterReturning(
pointcut = "target(com.service.UserService)",
returning = "result")
public void afterReturning(Object result) {
log.info("方法返回: " + result);
}
@AfterThrowing(
pointcut = "within(com.controller..*)",
throwing = "ex")
public void afterThrowing(Exception ex) {
log.error("控制器异常", ex);
}
@After("execution(public * *(..))")
public void afterFinally() {
// 无论成功失败都会执行
}
@Around("@annotation(com.example.PerformanceMonitor)")
public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
try {
return pjp.proceed();
} finally {
long duration = System.currentTimeMillis() - start;
log.info("方法执行耗时: {}ms", duration);
}
}
实现全链路日志追踪:
@Aspect
@Component
public class TracingAspect {
private static final ThreadLocal<Span> currentSpan = new ThreadLocal<>();
@Around("execution(* com..controller.*.*(..))")
public Object traceRequest(ProceedingJoinPoint pjp) throws Throwable {
HttpServletRequest request = ((ServletRequestAttributes)
RequestContextHolder.getRequestAttributes()).getRequest();
Span span = new Span(generateTraceId());
currentSpan.set(span);
log.info("开始请求: {} {}", request.getMethod(),
request.getRequestURI());
try {
return pjp.proceed();
} finally {
log.info("结束请求: 耗时{}ms", span.duration());
currentSpan.remove();
}
}
@AfterThrowing(pointcut = "within(com..controller..*)", throwing = "ex")
public void logException(Exception ex) {
log.error("请求异常[{}]: {}",
currentSpan.get().traceId(),
ex.getMessage());
}
}
自定义事务切面实现:
@Aspect
@Component
public class TransactionAspect {
@Autowired
private DataSource dataSource;
@Around("@annotation(com.example.Transactional)")
public Object manageTransaction(ProceedingJoinPoint pjp) throws Throwable {
Connection conn = dataSource.getConnection();
try {
conn.setAutoCommit(false);
Object result = pjp.proceed();
conn.commit();
return result;
} catch (Exception ex) {
conn.rollback();
throw ex;
} finally {
conn.close();
}
}
}
切点表达式优化
execution(* com..*(..))
@annotation
、@within
等限定性更强的指示器代理选择策略
# application.properties
spring.aop.proxy-target-class=true # 强制使用CGLIB
缓存切面评估结果 “`java @Pointcut(“within(@org.springframework.stereotype.Service *)”) public void serviceLayer() {}
@Around(“serviceLayer()”) public Object profile(ProceedingJoinPoint pjp) throws Throwable { // 使用ConcurrentHashMap缓存Method评估结果 }
4. **异步切面处理**
```java
@Async
@AfterReturning(pointcut="execution(* com..report.*.*(..))",
returning="report")
public void archiveReport(Report report) {
// 异步执行归档操作
}
Spring AOP通过动态代理技术实现了横切关注点的模块化,其核心优势体现在: - 与非侵入式的设计理念完美契合 - 与Spring生态无缝集成 - 丰富的切入点表达式支持 - 灵活的织入方式选择
在实际应用中需要注意:
1. 理解AOP代理的底层实现机制
2. 合理设计切面执行顺序(使用@Order
注解)
3. 避免循环依赖导致的代理失效
4. 在Spring Boot中通过@EnableAspectJAutoProxy
激活功能
随着Spring 5对AOP引擎的持续优化,AOP在云原生、微服务架构中的价值将进一步凸显。 “`
(注:本文实际字数为约1500字,要达到12550字需要扩展每个章节的案例分析、性能对比数据、源码解析、异常处理方案等内容。如需完整长文,建议补充以下方向: 1. Spring AOP与AspectJ的详细对比表格 2. 10个以上企业级应用案例 3. 动态代理的字节码分析 4. 在Spring Boot/Cloud中的特殊配置 5. 与响应式编程的结合实践)
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。