面向切面编程(AOP)
优点:
-
模块化横切关注点:
- 优点:AOP允许将横切关注点(如日志记录、事务管理、安全性检查等)从业务逻辑中分离出来,形成独立的模块(切面)。这提高了代码的模块化程度,使得业务逻辑更加清晰和简洁。
- 示例:通过将日志记录功能封装在切面中,业务逻辑代码不再需要包含日志记录的代码,从而减少了代码的重复和复杂性。
-
提高代码复用性:
- 优点:切面可以被多个模块共享和重用,避免了在每个模块中重复实现相同的功能。
- 示例:一个通用的性能监控切面可以应用于多个服务或方法,而不需要在每个方法中单独实现性能监控逻辑。
-
增强代码的可维护性:
- 优点:由于横切关注点被集中管理,修改和维护这些功能变得更加容易。例如,如果需要更改日志格式或事务管理策略,只需修改切面代码,而不需要修改每个业务逻辑模块。
- 示例:如果需要将日志记录从控制台输出改为写入文件,只需修改日志切面,而不需要触及业务逻辑代码。
-
提高开发效率:
- 优点:开发人员可以专注于业务逻辑的实现,而不需要花费大量时间在横切关注点的实现上。AOP工具和框架(如Spring AOP)提供了便捷的配置和使用方式,进一步提高了开发效率。
- 示例:通过使用Spring AOP,开发人员可以快速地为方法添加日志记录、事务管理等功能,而无需手动编写大量重复代码。
-
增强系统的可扩展性:
- 优点:AOP使得系统更容易扩展新功能。例如,可以通过添加新的切面来实现新的横切关注点,而不需要修改现有的业务逻辑代码。
- 示例:在系统中添加一个新的安全性检查切面,可以在不修改现有业务逻辑的情况下,为所有相关方法添加安全性检查功能。
缺点:
-
增加系统的复杂性:
- 缺点:引入AOP会增加系统的复杂性,特别是在理解和调试方面。切面的存在可能会使代码的执行流程变得不那么直观,增加了调试和排查问题的难度。
- 示例:在调试过程中,可能需要额外关注切面的影响,确保切面代码没有引入意外的行为或性能问题。
-
潜在的性能开销:
- 缺点:AOP的实现(如动态代理)可能会引入一定的性能开销。特别是在高并发或对性能要求极高的系统中,这种开销可能需要特别关注和优化。
- 示例:使用动态代理实现的AOP可能会在方法调用时引入额外的开销,特别是在频繁调用的情况下。
-
配置和管理的复杂性:
- 缺点:AOP的配置和管理可能会变得复杂,特别是在大型系统中。需要仔细管理切点、通知和切面的配置,确保它们正确地应用到目标对象上。
- 示例:在复杂的系统中,可能需要管理大量的切面和切点配置,确保它们不会相互冲突或产生意外的行为。
-
学习曲线:
- 缺点:对于不熟悉AOP的开发人员来说,学习和理解AOP的概念和使用方式可能需要一定的时间和精力。
- 示例:新加入团队的开发人员可能需要花费时间学习AOP的基本概念、Spring AOP的配置和使用方式等
案例:
1. 声明一个注解
/** 一般声明一个注解,都有写这几个基础的注解,复制即可。**/
@Target({ElementType.METHOD}) //标明自定义注解可作用的地方,指方法
@Retention(RetentionPolicy.RUNTIME)//存活阶段,RUNRIME:存在运行期,还有jvm,class文件级别
@Documented //作用域
@Inherited //可继承
public @interface RequiresIotOauth {
String value() default "";
}
2.编写切面
@Component
@Aspect
public class RequiresIotOAuthAspect {
@Resource
private AuthIotWebSocketHandler authIotWebSocketHandler;
/**
* 切入点
* 切入点为注解的完整路径。
*/
@Pointcut("@annotation(com.ruoyi.iot.util.iotOauth.RequiresIotOauth)")
public void requiresIotOAuth() {}
/**
* SpingAOP的通知共有五种:
* @Before: 前置通知在方法执行前执行
* @AfterReturning: 返回后通知,在目标方法执行后执行,如果出现异常不会执行
* @After: 后置通知,在目标方法返回结果之后执行,无论是否出现异常都会执行
* @AfterThrowing: 异常通知,在目标方法抛出异常后执行
* @Around: 环绕通知,围绕着方法执行
* @Around比较万能,尤其是配合ProceedingJoinPoint的使用。使AOP能做的事情更多了。
* ProceedingJoinPoint 只能在@Around中使用
* JoinPoint也可以获取入参(getArgs()),它可以用于@Before 和 @AfterReturning
* Proceedingjoinpoint 继承了 JoinPoint 。是在JoinPoint的基础上暴露出 proceed 这个方法。
*/
@Around("requiresIotOAuth()")
public Object before(ProceedingJoinPoint joinPoint) throws Throwable {
// 获取当前认证信息
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
String token = request.getHeader("authorization");
if (token == null || token.isEmpty()) {
return RV.fail(401, "权限不足");
}else {
// authIotWebSocketHandler.authenticate(token) 为自己实现的校验方法。
if (authIotWebSocketHandler.authenticate(token)){
// 权限校验通过,执行原方法
return joinPoint.proceed();
}
return RV.fail(401, "权限不足");
}
}
}
3. 使用
/**
* 故障排查原因
*/
@ApiOperation(value = "点位故障排查")
@PostMapping("/point/exception/analysis")
@RequiresIotOauth()
public RV<List<IotPointExceptionVO>> pointExceptionAnalysis(@Valid @NotEmpty @RequestParam(value = "pointCode") String pointCode){
return openApiService.pointExceptionAnalysis(pointCode);
}