EnumSet做权限校验可实现严格O(1)判定,核心是权限枚举按声明顺序映射位索引,预构建不可变集并用containsAll进行位运算比对。
用 EnumSet 做权限校验,核心在于把权限抽象为枚举,再利用其底层位运算特性实现真正的 O(1) 判定——不是“接近 O(1)”,而是实实在在的常数时间查集操作。
EnumSet 内部使用 long 或 bit vector 存储,但前提是枚举值的声明顺序决定其位索引。JVM 会按声明顺序给每个枚举常量分配一个序号(ordinal()),EnumSet 正是基于这个序号做位标记。所以权限枚举必须显式按 2 的幂次设计,确保每个权限独占一位:
READ, WRITE, DELETE(ordinal=0,1,2 → 位重叠,无法位运算区分)READ(1), WRITE(2), DELETE(4), ADMIN(8),并重载 ordinal() 或直接靠构造器控制顺序enum Permission { READ, WRITE, DELETE, ADMIN },不赋值,只依赖声明顺序,并保证总数 ≤64(RegularEnumSet 用 long;超 64 会退化为 JumboEnumSet,仍 O(1),但内存略增)每次请求都 new 一个 EnumSet 是低效的。应在用户登录或权限加载时,一次性构建不可变权限集:
EnumSet.copyOf(Collection<Permission>) 或 EnumSet.of(p1, p2, ...) 构建EnumSet.unmodifiableEnumSet(...),防止误修改假设接口所需权限是 EnumSet.of(READ, WRITE),用户权限集是 userPerms,判定逻辑就是:
boolean hasAccess = userPerms.containsAll(requiredPerms);
这行代码背后不是遍历,而是:
requiredPerms 所有元素对应位取出,组成 maskuserPerms 的位向量执行 (userBits & requiredMask) == requiredMask
在 HandlerInterceptor.preHandle 中,从 request 提取用户上下文,拿到预构建的 EnumSet<Permission>,再比对当前请求路径绑定的权限元数据(可存在注解、配置中心或路由表中):
@RequirePermission({READ, WRITE}) 标记 Controller 方法,通过 ReflectionUtils 提前解析并缓存到 Map<String, EnumSet> 中pathToPermissions.get(request.getRequestURI()).containsAll(userPerms) —— 注意这里应是 userPerms.containsAll(required),方向别反不复杂但容易忽略:EnumSet 的高性能完全依赖枚举定义规范和预构建习惯。一旦写成动态 new 或权限乱序,就退化成普通 Set 查找。