
我们可以通过一个简单的场景演变来回答这个问题。
场景:一个简单的用户服务
假设我们有一个 UserService,它包含几个核心的业务方法:
public class UserServiceImpl implements UserService {
@Override
public void addUser(String username) {
// 核心业务逻辑:向数据库插入一个新用户
System.out.println("数据库操作:新增用户 " + username);
}
@Override
public void deleteUser(int userId) {
// 核心业务逻辑:从数据库删除一个用户
System.out.println("数据库操作:删除用户 " + userId);
}
}
现在,产品经理提出了几个新需求:
- 在每个方法执行前,打印一下方法入参,方便调试。
- 记录每个方法执行花费了多长时间。
- 在执行数据库操作前,需要检查当前操作员是否有权限。
阶段一:最直观的实现 (没有 AOP)
我们直接在每个方法里添加这些功能代码:
public class UserServiceImpl implements UserService {
@Override
public void addUser(String username) {
// --- 非核心业务代码开始 ---
System.out.println("[日志] 方法 addUser() 被调用,参数: " + username);
long startTime = System.currentTimeMillis();
// 权限检查
if (!SecurityUtil.hasPermission("addUser")) {
throw new SecurityException("权限不足!");
}
// --- 非核心业务代码结束 ---
// 核心业务逻辑
System.out.println("数据库操作:新增用户 " + username);
// --- 非核心业务代码开始 ---
long endTime = System.currentTimeMillis();
System.out.println("[性能] 方法 addUser() 执行耗时: " + (endTime - startTime) + "ms");
// --- 非核心业务代码结束 ---
}
@Override
public void deleteUser(int userId) {
// --- 非核心业务代码开始 ---
System.out.println("[日志] 方法 deleteUser() 被调用,参数: " + userId);
long startTime = System.currentTimeMillis();
// 权限检查
if (!SecurityUtil.hasPermission("deleteUser")) {
throw new SecurityException("权限不足!");
}
// --- 非核心业务代码结束 ---
// 核心业务逻辑
System.out.println("数据库操作:删除用户 " + userId);
// --- 非核心业务代码开始 ---
long endTime = System.currentTimeMillis();
System.out.println("[性能] 方法 deleteUser() 执行耗时: " + (endTime - startTime) + "ms");
// --- 非核心业务代码结束 ---
}
}
这样做暴露出了几个 致命的痛点:
-
代码重复 (Code Duplication):日志、性能监控、权限检查的代码在
addUser和deleteUser方法中几乎完全一样。如果再来一个updateUser方法,我们还得再复制粘贴一遍。 -
代码分散 (Code Scattering):日志功能相关的代码,分散在系统的各个角落,没有集中管理。如果现在想把日志输出格式从
[日志]改为[INFO],你需要修改所有的方法,非常痛苦且容易遗漏。 -
核心业务与非核心业务高度耦合 (High Coupling):
UserServiceImpl的主要职责是处理用户相关的业务。但现在,它被迫“关心”日志、性能和安全这些它本不该关心的事情。这严重违反了软件设计的 单一职责原则 (Single Responsibility Principle)。
这些与核心业务无关,但又需要在多个地方重复使用的功能(如日志、事务、安全、性能监控),我们称之为 横切关注点 (Cross-Cutting Concerns)。
阶段二:AOP 的闪亮登场
AOP (Aspect-Oriented Programming, 面向切面编程) 的诞生就是为了优雅地解决上述问题。
它的核心思想是:将横切关注点与业务主逻辑分离,实现解耦。
AOP 允许我们把这些“横切关注点”的代码逻辑,提取到一个独立的模块中,这个模块我们称之为 “切面” (Aspect)。然后,我们通过配置告诉框架:“请在‘某个地方’(比如 UserService 的所有 public 方法执行前/后)自动应用这个‘切面’里的逻辑。”
使用 AOP 改造后的代码
1. 保持纯净的业务逻辑
UserServiceImpl 回到了它最开始的纯净状态,只关心自己的核心职责。
public class UserServiceImpl implements UserService {
@Override
public void addUser(String username) {
System.out.println("数据库操作:新增用户 " + username);
}
@Override
public void deleteUser(int userId) {
System.out.println("数据库操作:删除用户 " + userId);
}
}
这代码看起来多么清爽!
2. 定义一个独立的“切面”
我们创建一个 SystemArchitectureAspect 来统一管理日志、性能和安全。
@Aspect // 声明这是一个切面
@Component
public class SystemArchitectureAspect {
// 定义一个“切点”,匹配 UserService 的所有 public 方法
@Pointcut("execution(public * com.example.UserService.*(..))")
public void userServiceMethods() {}
// “前置通知”:在目标方法执行前运行
@Before("userServiceMethods()")
public void logBefore(JoinPoint joinPoint) {
System.out.println("[日志] 方法 " + joinPoint.getSignature().getName() + "() 被调用,参数: " + Arrays.toString(joinPoint.getArgs()));
}
// “环绕通知”:可以控制目标方法的执行,功能最强大
@Around("userServiceMethods()")
public Object profile(ProceedingJoinPoint pjp) throws Throwable {
// 权限检查
if (!SecurityUtil.hasPermission(pjp.getSignature().getName())) {
throw new SecurityException("权限不足!");
}
long startTime = System.currentTimeMillis();
Object result = pjp.proceed(); // 调用原始的目标方法
long endTime = System.currentTimeMillis();
System.out.println("[性能] 方法 " + pjp.getSignature().getName() + "() 执行耗时: " + (endTime - startTime) + "ms");
return result;
}
}
总结:所以,为什么需要 AOP?
AOP 不是一门新的技术,而是一种编程思想,它帮助我们解决了传统面向对象编程 (OOP) 难以处理的“横切关注点”问题。
一句话总结:AOP 让我们能够将散布在应用中多个角落的通用功能(如日志、事务、安全)聚合到一个地方进行统一管理,从而让业务代码更纯粹、系统模块化更清晰、代码可维护性和复用性更高。
它就像给你的程序装上了一个“插件系统”,你可以在不修改任何一行核心业务代码的情况下,动态的为系统添加或移除功能。Spring 框架的声明式事务 (@Transactional) 就是 AOP 最经典的应用。

被折叠的 条评论
为什么被折叠?



