使用Enumtypes作为@ RolesAllowed-Annotation的值参数

我正在开发一个Java企业应用程序,目前正在执行Java EE安全性的东西来限制特定function对特定用户的访问。 我configuration了应用程序服务器和所有东西,现在我使用RolesAllowed注释来保护这些方法:

@Documented @Retention (RUNTIME) @Target({TYPE, METHOD}) public @interface RolesAllowed { String[] value(); } 

当我使用这样的注释时,它工作正常:

 @RolesAllowed("STUDENT") public void update(User p) { ... } 

但这不是我想要的,因为我必须在这里使用一个string,重构变得困难,错字可能发生。 所以,而不是使用一个string,我想使用一个枚举值作为这个注释的参数。 Enum看起来像这样:

 public enum RoleType { STUDENT("STUDENT"), TEACHER("TEACHER"), DEANERY("DEANERY"); private final String label; private RoleType(String label) { this.label = label; } public String toString() { return this.label; } } 

所以我试图使用Enum作为这样的参数:

 @RolesAllowed(RoleType.DEANERY.name()) public void update(User p) { ... } 

但是,然后我得到了下面的编译器错误,虽然Enum.name只是返回一个string(它总是不变的,不是吗?)。

注释属性RolesAllowed.value的值必须是常量expression式

接下来,我尝试添加一个额外的最终string到我的枚举:

 public enum RoleType { ... public static final String STUDENT_ROLE = STUDENT.toString(); ... } 

但是,这也不能作为一个参数,导致相同的编译器错误:

 // The value for annotation attribute RolesAllowed.value must be a constant expression @RolesAllowed(RoleType.STUDENT_ROLE) 

我怎样才能达到我想要的行为? 我甚至实现了我自己的拦截器,使用我自己的注释,这是美丽的,但有太多的代码行这样的小问题。

免责声明

这个问题最初是一个斯卡拉问题。 我发现Scala不是问题的根源,所以我首先尝试在Java中这样做。

我不认为你使用枚举的方法会起作用。 我发现编译器错误消失了,如果我把最后一个例子中的STUDENT_ROLE字段改为一个常量string,而不是一个expression式:

 public enum RoleType { ... public static final String STUDENT_ROLE = "STUDENT"; ... } 

然而,这意味着枚举值不会在任何地方使用,因为您将在注释中使用string常量。

在我看来,如果你的RoleType类只包含一堆静态的最终string常量,你会更好。


为了看看为什么你的代码没有编译,我看了一下Java语言规范 (JLS)。 用于注释的JLS指出,对于具有typesT和值V的参数的注释,

如果T是一个原始types或StringV是一个常量expression式。

不断的expression包括,除其他外,

表单TypeName的合格名称。 引用常量variables的标识符

并定义一个常量variables

一个原始types或types为String的variables,它是最终的,并用一个编译时常量expression式进行初始化

这是一个使用附加界面和元注释的解决scheme。 我已经包含了一个实用工具类来帮助做reflection,从一组注释中获取angular色types,并对其进行一些testing:

 /** * empty interface which must be implemented by enums participating in * annotations of "type" @RolesAllowed. */ public interface RoleType { public String toString(); } /** meta annotation to be applied to annotations that have enum values implementing RoleType. * the value() method should return an array of objects assignable to RoleType*. */ @Retention(RetentionPolicy.RUNTIME) @Target({ANNOTATION_TYPE}) public @interface RolesAllowed { /* deliberately empty */ } @RolesAllowed @Retention(RetentionPolicy.RUNTIME) @Target({TYPE, METHOD}) public @interface AcademicRolesAllowed { public AcademicRoleType[] value(); } public enum AcademicRoleType implements RoleType { STUDENT, TEACHER, DEANERY; @Override public String toString() { return name(); } } public class RolesAllowedUtil { /** get the array of allowed RoleTypes for a given class **/ public static List<RoleType> getRoleTypesAllowedFromAnnotations( Annotation[] annotations) { List<RoleType> roleTypesAllowed = new ArrayList<RoleType>(); for (Annotation annotation : annotations) { if (annotation.annotationType().isAnnotationPresent( RolesAllowed.class)) { RoleType[] roleTypes = getRoleTypesFromAnnotation(annotation); if (roleTypes != null) for (RoleType roleType : roleTypes) roleTypesAllowed.add(roleType); } } return roleTypesAllowed; } public static RoleType[] getRoleTypesFromAnnotation(Annotation annotation) { Method[] methods = annotation.annotationType().getMethods(); for (Method method : methods) { String name = method.getName(); Class<?> returnType = method.getReturnType(); Class<?> componentType = returnType.getComponentType(); if (name.equals("value") && returnType.isArray() && RoleType.class.isAssignableFrom(componentType)) { RoleType[] features; try { features = (RoleType[]) (method.invoke(annotation, new Object[] {})); } catch (Exception e) { throw new RuntimeException( "Error executing value() method in " + annotation.getClass().getCanonicalName(), e); } return features; } } throw new RuntimeException( "No value() method returning a RoleType[] type " + "was found in annotation " + annotation.getClass().getCanonicalName()); } } public class RoleTypeTest { @AcademicRolesAllowed({DEANERY}) public class DeaneryDemo { } @Test public void testDeanery() { List<RoleType> roleTypes = RolesAllowedUtil.getRoleTypesAllowedFromAnnotations(DeaneryDemo.class.getAnnotations()); assertEquals(1, roleTypes.size()); } } 

这个怎么样?

 public enum RoleType { STUDENT(Names.STUDENT), TEACHER(Names.TEACHER), DEANERY(Names.DEANERY); public class Names{ public static final String STUDENT = "Student"; public static final String TEACHER = "Teacher"; public static final String DEANERY = "Deanery"; } private final String label; private RoleType(String label) { this.label = label; } public String toString() { return this.label; } } 

而在注释中,你可以使用它

 @RolesAllowed(RoleType.Names.DEANERY) public void update(User p) { ... } 

一个小问题是,对于任何修改,我们需要在两个地方改变。 但由于它们在同一个文件中,所以不太可能错过。 作为回报,我们获得了不使用原始string和避免复杂机制的好处。

或者这听起来很愚蠢? 🙂