Spring Boot中利用AOP与自定义注解优雅处理超级管理员分支逻辑
在Spring Boot项目中,针对超级管理员的特殊权限逻辑若采用大量if-else会导致代码臃肿。本文介绍一种基于自定义注解SuperDirection和SuperDirectionHandler,结合AspectJ切面编程的动态代理方案。通过解析注解表达式,自动切换至特备业务实现类,实现业务逻辑解耦,提升代码可维护性与扩展性。
背景与痛点
在引入登录模块与菜单权限管理后,系统通常需要根据用户角色动态加载数据。传统做法是在Service层判断当前用户是否为超级管理员,从而执行不同的数据库查询逻辑:
if (SecurityUtils.getSubject().hasRole(RoleList.SUPERADMIN)) {
listemp = customMapper.selectRootMenusByRoleIdList(null, null, null, null);
} else {
listemp = customMapper.selectRootMenusByRoleIdList(roleList, oauthClientId, null, moduleCodes);
}
这种硬编码的分支判断虽然直观,但当系统中存在多个需要特殊处理的场景时,会导致核心业务代码中充斥大量的条件判断,降低可读性并增加维护成本。我们需要一种更优雅的方式,将“特殊规则”从主流程中剥离。
核心设计思路
借鉴Spring Cloud Feign的容灾降级思想,我们可以定义一套“默认实现”与“替补实现”机制。通过自定义注解标记哪些方法需要参与分支判断,并利用Spring AOP在运行时动态选择正确的Bean进行执行。
第一步:定义自定义注解
我们需要两个关键注解来建立连接:
SuperDirection:用于标注需要进行分支判断的方法或类。其value属性用于存储动态判断表达式(如角色判断条件)。SuperDirectionHandler:用于标注充当“替补者”的实现类Bean,表示该类专门处理特定条件下的逻辑(如超级管理员逻辑)。
SuperDirection注解
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SuperDirection {
String value() default StringUtils.EMPTY;
}
SuperDirectionHandler注解
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface SuperDirectionHandler {
}
这两个注解建议放置在项目的核心基础模块(如 org.framework.core),以便全局复用。
第二步:实现切面拦截逻辑
利用Spring AOP的环绕通知(Around Advice),拦截所有被 @SuperDirection 标记的目标方法。在方法执行前,动态检索是否存在符合条件的“替补”Bean。
定义切点
@Pointcut("@annotation(com.github.zxhtom.core.annotaion.SuperDirection) || @within(com.github.zxhtom.core.annotaion.SuperDirection)")
public void direction() { }
环绕通知核心逻辑
切面需获取目标接口类型,并在Spring容器中找到同时满足以下条件的Bean:
- 实现了该接口。
- 本身被
@SuperDirectionHandler标记。
@Around("direction()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
Class>[] inters = pjp.getTarget().getClass().getInterfaces();
for (Class> inter : inters) {
// 获取该接口的所有实现Bean
Map beansOfType = ApplicationContextUtil.getApplicationContext().getBeansOfType(inter);
// 获取被替补处理器注解标记的所有Bean
Map beansWithAnnotation = ApplicationContextUtil.getApplicationContext().getBeansWithAnnotation(SuperDirectionHandler.class);
for (Map.Entry entry : beansOfType.entrySet()) {
// 找到既是接口实现,又是替补处理器的Bean
if (beansWithAnnotation.containsKey(entry.getKey())) {
try {
return doOthersHandler(entry.getValue(), pjp); // 执行替补逻辑
} catch (Exception e) {
log.error("分支执行失败,回退至原有逻辑", e);
}
}
}
}
// 未找到符合条件的替补Bean,执行原方法
return pjp.proceed();
}
第三步:动态表达式解析与条件判断
确定使用替补Bean后,需根据 @SuperDirection 上的 value 表达式决定是否真正执行。如果表达式结果为真,则调用替补Bean的方法;否则执行原Bean方法。
private boolean selectAnnotationChoiceDo(SuperDirection superDirection) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
String value = superDirection.value();
// 默认空值代表直接判断是否为超级管理员
if (StringUtils.isEmpty(value)) {
return onlineSecurity.getRoleNames().contains(MaltcloudConstant.SUPERADMIN);
}
// 解析表达式字符串,反射调用对应的判断方法
MethodInfo info = selectInfoFromExpression(value);
Method declaredMethod = expression.getClass().getDeclaredMethod(info.getMethodName(), String.class);
Object invoke = declaredMethod.invoke(expression, info.getArgs());
return invoke != null && "true".equals(invoke.toString());
}
表达式解析接口(RootChoiceExpression)允许灵活扩展判断条件:
public interface RootChoiceExpression {
boolean haslogined();
boolean hasRole(String role);
boolean hasAnyRole(String... roles);
}
实际演示
通过Controller、Service层的配合,体验这一无侵入式改造的效果。
Controller层
注入普通的Service实现(非替补者),但在调用时若满足条件,切面会自动介入:
@RestController
@RequestMapping("/demo/common")
public class CommonController {
// 明确注入普通实现,而非替补实现
@Qualifier("commonTestServiceImpl")
@Autowired
CommonTestService commonTestService;
@RequestMapping(value = "/test", method = RequestMethod.GET)
public void test() {
commonTestService.test();
}
}
Service层实现
普通实现类(默认逻辑):
@Service("commonTestServiceImpl")
@SuperDirection(value = "") // 空值表示默认触发超级管理员分支
public class CommonTestServiceImpl implements CommonTestService {
@Override
public void test() {
System.out.println("执行普通用户逻辑...");
}
}
超级管理员实现类(替补逻辑):
@Service
@SuperDirectionHandler // 标记为替补处理器
public class CommonTest2ServiceImpl implements CommonTestService {
@Override
public void test() {
System.out.println("执行超级管理员特殊逻辑...");
}
}
运行效果验证
当当前用户不拥有超级管理员角色时,系统执行 CommonTestServiceImpl;当赋予超级管理员角色时,切面捕获到条件匹配,自动转而执行 CommonTest2ServiceImpl 的逻辑,而Controller层代码无需任何修改。
总结
通过上述AOP与自定义注解的方案,我们将原本散落在各处的“超级管理员特殊判断”收敛到了切面中。业务开发人员在编写普通逻辑时,只需关注正常的数据访问;而特殊角色的处理,可以通过新增一个带 @SuperDirectionHandler 的Impl类并通过注解配置来实现。这不仅降低了代码耦合度,也完美契合了开闭原则,便于后续对更多复杂角色策略进行扩展。
以上就是Spring Boot通用分支处理超级管理员权限逻辑的详细内容,更多关于Spring Boot高级技巧与架构设计的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









