没有Spring AOP的话,Java代码很难保持简洁

B站影视 日本电影 2025-05-17 23:00 2

摘要:如果你曾经在Java企业级代码库中工作过,你很可能遇到过一个Controller或Service做了太多事情 — 业务逻辑、日志记录、验证、认证、指标统计、重试等。所有这些都在一个方法中。

如果你曾经在Java企业级代码库中工作过,你很可能遇到过一个Controller或Service做了太多事情 — 业务逻辑、日志记录、验证、认证、指标统计、重试等。所有这些都在一个方法中。

结果如何?代码难以阅读,更难测试,几乎不可能干净地扩展。

所以,我要做出一个大胆的声明:
在企业级应用中,如果没有面向切面编程(AOP),编写干净、可维护的Java代码是不可能的。

让我来解释原因,以及如何使用AOP为最混乱的Spring后端带来清晰和秩序。

**面向切面编程(AOP)**是一种编程范式,它允许你将横切关注点(日志记录、安全、事务等)与业务逻辑分开模块化。

在Java中,AOP通常使用注解和代理来实现 — 特别是通过Spring AOP或AspectJ。

让我们看一个典型的Spring服务:

public class PaymentService {

public voidprocessPayment(Paymentrequest request){

log.info("Processing payment {}", request);

if(!validator.isValid(request)){

throw newValidationException("Invalid payment request");

}

metrics.increment("payment.attempts");

try{

}catch(Exception ex){

log.error("Failed to charge payment", ex);

}

}

}

现在想象这个方法在20个服务中。每一个都混合了:

• 日志记录• 验证• 监控• 错误处理• 重试逻辑

每个关注点都被重复和纠缠在一起,违反了单一职责原则,使得关注点的清晰分离变得不可能。

@Aspect

@Component

public class LoggingAspect {

@Before("execution(* com.example.service.*.*(..))")

public voidlogBefore(JoinPoint joinPoint){

log.info("Calling: {}", joinPoint.getSignature);

}

@AfterReturning(pointcut = "execution(* com.example.service.*.*(..))")

public voidlogAfter(JoinPoint joinPoint){

log.info("Completed: {}", joinPoint.getSignature);

}

}

@Aspect

@Component

public class ValidationAspect {

@Before("@annotation(Validate)")

public voidvalidateRequest(JoinPoint joinPoint){

for(Object arg : joinPoint.getArgs){

if(arg instanceof Validatable){

((Validatable) arg).validate; // 你的自定义验证逻辑

}

}

}

}

指标切面@Aspect

@Component

public class MetricsAspect {

@Around("execution(* com.example.service.*.*(..))")

public Object timeExecution(ProceedingJoinPoint joinPoint) throws Throwable {

long start = System.nanoTime;

try{

return joinPoint.proceed;

} finally {

long duration = System.nanoTime - start;

metrics.record(joinPoint.getSignature.toShortString, duration);

}

}

}

干净的服务代码

现在你的服务只关注业务逻辑:

@Validate
public class PaymentService {

public void processPayment(PaymentRequest request) {
paymentGateway.charge(request);
}
}

这才是干净代码应该有的样子 — 简短、专注且可测试

| 指标 | 不使用AOP(内联代码) | 使用AOP |
| | | -------- |
| 业务代码行数 | 1300+ | 400 |
| 平均方法执行时间(ns) | 2200 | 2500 |
| 关注点的可重用性 | 重复 | 共享 |
| 单元测试覆盖率 | 45% | 85% |

AOP每个方法增加了约300ns的开销。但可维护性、可重用性和可测试性得到了显著改善。

只有在过度使用时才会如此。保持切面小而专注,并始终为它们编写测试。Spring的AOP日志功能(@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true))使调试变得可预测。

当有文档记录且保持一致时,魔幻就变得可维护。相比之下,在每个方法中硬编码每个关注点是你将来必须偿还的技术债务。

在这些情况下,显式装饰器或函数组合可能更好。

最后的思考

当正确使用时:

如果你在2025年还在编写企业级Java代码,仍然在每个方法中手动复制日志记录、验证和指标,你写的不是干净的代码 — 你写的是杂乱的代码。

来源:码农看看

相关推荐