SpringBoot 项目中 AOP 的使用实例
1. 背景
在最近做的项目中,有一个需求需要用到 AOP,具体说明如下:
项目中有一个方法用于判断角色是否拥有权限,现在这个判断是否拥有权限的逻辑有变,但是又没办法改动这个方法,所以用到了 AOP 的解决方案。
2. 细节
id | parent_id |
---|---|
角色id | 角色上一层的角色的id |
之前,角色分为三层,我们使用的都是第三层的角色进行登录,所以判断一个角色是否有权限的方式是利用这个第三层角色向上查两层得到第一层角色,如果这个第一层角色符合条件,那么这个第三层角色拥有权限。
所以我们需要一个方法可以利用第三层角色查到第一层角色。基于此,之前这个方法是一个 Mapper 方法(只能改 Mybatis 语句,不能加 Java 代码),直接在 Sql 层面写死利用这个角色向上查两层得到第一层角色的信息。
但是现在的逻辑改变了,登录的角色有可能是第二层的角色,也可能是第三第四层的角色,所以不能写死。
所以,我们要改变方法的逻辑,但是在 Sql 层面做到灵活化是很难做到的(主要想要是利用循环来解决灵活化的问题),而且这个方法已经在项目中广泛使用了,所以写一个新的方法,然后在使用旧方法的地方一个个去改为使用新方法是艰难的。
3. 解决方案
所以,我们以这个旧方法为切入点,写了一个切面来“增强”这个方法。
其实就是使用了 Around 对旧方法进行代理增强,在切入点中不执行 joinPoint.proceed(),也就是不会再执行旧方法,直接调用新方法并且返回数据,这个方式成功解决了上述问题。
4. 代码
切面类:
@Slf4j
@Aspect
@Component
public class OrganInfoAspect {
@Autowired
private OrganInfoService organInfoService;
// 这里使用的是用自定义注解来标注切入点
// 下面指定的路径是自定义注解类 Agent
@Pointcut("@annotation(com.包名.包名.包名.自定义注解类所在包包名.Agent)")
public void organInfoPointCut() {
}
// 本来我们就是想要用新方法来取代就方法,所以不会用 pjp.proceed() 来调用旧方法
// 而是直接执行新方法后返回想要的数据
@Around("organInfoPointCut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
Object[] args = pjp.getArgs();
String param = Arrays.toString(args);
String userId = param.substring(1, param.length() - 1);
Object organInfo = organInfoService.findOrganInfo(userId);
Map<String, String> resultMap = ReflectObjectUtil.ObjectToMap(organInfo);
return resultMap;
}
}
自定义注解类:
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Agent {
}
给旧方法加上注解标注这里需要被增强:
@Agent
HashMap<String, Object> selectRole(String userId);
5. 总结
这个实例说明的就是,当一个方法被弃用了,需要更新为别的一个方法。
但是这个方法在很多地方被用了。
这个时候,就可以用 Around 增强方法来实现“鸠占鹊巢”,直接执行新方法,不执行旧方法。