公司的一些BadTasteCode
经典错误1:
1、 在controller里面进行try catch,捕捉异常
2、把日志打在 controller 里面,这里更好的方式,是使用 aop 进行统一的日志管理
# 使用 AOP 优化 Controller 中的异常处理与日志记录
在日常的开发过程中,尤其是基于 Spring 框架的 Java Web 应用中,开发者时常会犯的一个经典错误是 在 Controller 中进行 try-catch 捕获异常,并在 Controller 中记录日志。这个做法虽然能够满足基本的异常处理需求,但从系统设计和代码质量的角度来看,存在诸多不足。
本文将系统性阐述为什么这种方式不可取,以及为什么使用 AOP(面向切面编程) 来处理异常和日志记录是一种更优雅的解决方案。
# 一、Controller 中 try-catch 捕获异常的不足
# 1. 代码冗余
在每个 Controller 方法中都写 try-catch
块,显然会导致大量冗余代码,尤其当项目规模较大、接口数量众多时,这种重复操作会让代码的可读性和维护性大打折扣。
问题:
@RestController
public class UserController {
@GetMapping("/user/{id}")
public ResponseEntity<User> getUserById(@PathVariable Long id) {
try {
// 业务逻辑
User user = userService.getUserById(id);
return ResponseEntity.ok(user);
} catch (Exception e) {
// 异常处理
e.printStackTrace(); // 打印日志
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
每个 API 都需要进行异常捕获,代码冗余且难以统一管理。
# 2. 违反单一职责原则
在控制器中捕获异常和记录日志实际上混淆了控制器的职责。控制器的主要职责是处理 HTTP 请求并调用业务逻辑层。而异常处理、日志记录等都是与业务无关的通用逻辑,应该抽离出来,以保持代码模块的职责单一性。
# 3. 缺乏统一异常处理策略
当项目中有多个 Controller 时,如果每个 Controller 中都手动处理异常,很容易导致异常处理逻辑的多样性和不一致性,缺乏一个统一的异常处理策略,这不利于代码的可维护性和扩展性。
# 二、Controller 中日志记录的不足
# 1. 日志代码分散,难以管理
如果在每个 Controller 方法中手动添加日志记录代码,不仅造成代码重复,且日志逻辑分散在多个地方,难以统一管理和调整。不同开发人员在记录日志时可能有不同的习惯,导致日志格式不统一,日志级别不一致,给后期运维和调试带来困扰。
# 2. 代码污染
控制器的核心职责是业务逻辑的处理,而非日志记录。过多的日志代码会污染控制器,使其职责变得混乱,并且降低代码的可读性。
# 三、AOP 的引入:解决方案
面向切面编程(AOP) 是一种编程范式,旨在通过将横切关注点(如日志记录、事务管理、异常处理)从业务逻辑中分离出来,避免这些横切关注点散布在多个地方。AOP 的核心思想是:将这类通用逻辑通过 切面(Aspect) 统一管理。
# 1. AOP 异常处理
AOP 可以通过定义一个切面,在所有的 Controller 方法执行时对其进行监控,一旦捕获到异常,则统一处理。
@Aspect
@Component
public class ExceptionHandlerAspect {
@AfterThrowing(pointcut = "execution(* com.example.controller..*(..))", throwing = "ex")
public void handleControllerException(JoinPoint joinPoint, Throwable ex) {
// 记录异常日志
log.error("Exception in method: " + joinPoint.getSignature().getName() + ", with cause: " + ex.getMessage());
// 可以在此统一返回某种标准格式的错误响应
// 或者重新抛出自定义异常让全局异常处理器处理
}
}
2
3
4
5
6
7
8
9
10
11
12
13
通过上述切面,所有 Controller 的异常都可以统一捕获和处理,而不需要在每个方法中手动写 try-catch
逻辑。
# 2. AOP 日志记录
类似地,日志记录也可以通过 AOP 实现。通过一个日志切面,可以在所有 Controller 方法执行时记录请求参数、执行结果以及方法耗时等信息。
@Aspect
@Component
public class LoggingAspect {
@Around("execution(* com.example.controller..*(..))")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
// 记录请求参数
log.info("Start method: " + joinPoint.getSignature().getName() + ", args: " + Arrays.toString(joinPoint.getArgs()));
// 执行业务逻辑
Object result = joinPoint.proceed();
// 记录响应结果及耗时
log.info("End method: " + joinPoint.getSignature().getName() + ", result: " + result + ", time: " + (System.currentTimeMillis() - startTime) + " ms");
return result;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
通过这样的切面,开发者可以减少日志记录的重复工作,并且确保所有日志格式和级别的一致性。统一的日志管理不仅提高了代码的可读性,还大大简化了系统的维护工作。
# 四、AOP 带来的优势
# 1. 统一管理
通过 AOP,我们可以将日志记录、异常处理这些横切关注点统一管理,避免了它们分散在代码的各个角落,提升了代码的可维护性和一致性。
# 2. 降低代码耦合
使用 AOP 将日志记录和异常处理从业务代码中分离,降低了代码的耦合度,使得代码更为简洁和易读。Controller 只需专注于业务逻辑,符合单一职责原则。
# 3. 提升可维护性
当需要修改日志记录或异常处理策略时,只需在切面中做相应的调整,而不必逐一修改每个 Controller 方法中的代码,极大地提高了系统的可维护性和扩展性。
# 五、总结
将异常处理和日志记录等横切关注点统一交由 AOP 来处理,既能减少代码的冗余、提升可读性,又能保证系统的统一性和可维护性。使用 AOP 进行日志管理和异常处理,不仅是 Spring 框架中的最佳实践之一,也是大中型企业应用中普遍采用的系统设计模式。
通过这样的改进,可以让项目变得更加清晰、简洁,也为日后的扩展和维护提供了极大的便利。