Java 将枚举类型用作@RoleAllowed批注的值参数

Java 将枚举类型用作@RoleAllowed批注的值参数,java,jakarta-ee,enums,annotations,java-ee-6,Java,Jakarta Ee,Enums,Annotations,Java Ee 6,我正在开发一个Java企业应用程序,目前正在进行JavaEE安全性工作,以限制特定用户对特定功能的访问。我配置了应用服务器和所有东西,现在我使用RoleAllowed注释来保护这些方法: @Documented @Retention (RUNTIME) @Target({TYPE, METHOD}) public @interface RolesAllowed { String[] value(); } 当我像这样使用注释时,效果很好: @RolesAllowed("STUDENT")

我正在开发一个Java企业应用程序,目前正在进行JavaEE安全性工作,以限制特定用户对特定功能的访问。我配置了应用服务器和所有东西,现在我使用RoleAllowed注释来保护这些方法:

@Documented
@Retention (RUNTIME)
@Target({TYPE, METHOD})
public @interface RolesAllowed {
    String[] value();
}
当我像这样使用注释时,效果很好:

@RolesAllowed("STUDENT")
public void update(User p) { ... }
但这不是我想要的,因为我必须在这里使用字符串,重构变得很困难,而且可能会出现打字错误。因此,我希望使用枚举值作为此注释的参数,而不是使用字符串。枚举如下所示:

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;
    }
}
@RolesAllowed(RoleType.DEANERY.name())
public void update(User p) { ... }
因此,我尝试将枚举用作如下参数:

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;
    }
}
@RolesAllowed(RoleType.DEANERY.name())
public void update(User p) { ... }
但是我得到了以下编译器错误,尽管Enum.name只返回一个字符串(它总是常量,不是吗?)

批注属性RolesAllowed.value的值必须是常量表达式`

我尝试的下一件事是向我的枚举添加一个附加的最终字符串:

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\u ROLE
字段更改为常量字符串,而不是表达式,编译器错误就会消失:

public enum RoleType { 
  ...
  public static final String STUDENT_ROLE = "STUDENT";
  ...
}
然而,这意味着枚举值不会在任何地方使用,因为您将在注释中使用字符串常量

在我看来,如果您的
RoleType
类只包含一组静态的最终字符串常量,那么您的情况会更好


为了了解代码没有编译的原因,我查看了(JLS)。JLS for声明,对于具有类型为T且值为V的参数的注释

如果T是基元类型或
字符串
,则V是常量表达式

A除其他事项外,包括:

表单TypeName的限定名称。引用常量变量的标识符

a的定义是

一种原语类型或
字符串类型的变量,它是最终变量,并用编译时常量表达式初始化


这是一个使用附加接口和元注释的解决方案。我已经包含了一个实用程序类来帮助进行反射,以从一组注释中获取角色类型,并对其进行了一些测试:

/**
 * 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());
    }
}
/**
*必须由参与的枚举实现的空接口
*允许“type”@roles注释。
*/
公共接口角色类型{
公共字符串toString();
}
/**要应用于具有实现角色类型的枚举值的批注的元批注。
*value()方法应返回可分配给RoleType*的对象数组。
*/
@保留(RetentionPolicy.RUNTIME)
@目标({ANNOTATION_TYPE})
public@接口角色允许{
/*故意清空*/
}
@角色被允许
@保留(RetentionPolicy.RUNTIME)
@目标({TYPE,METHOD})
允许公共@接口角色{
public AcademicRoleType[]value();
}
公共枚举角色类型实现了角色类型{
学生、教师、院长;
@凌驾
公共字符串toString(){
返回名称();
}
}
公共类角色AllowedUtil{
/**获取给定类允许的角色类型数组**/
公共静态列表GetRoleTypeAllowedFromAnnotations(
注释[]注释){
List roleTypesAllowed=新建ArrayList();
用于(注释:注释){
如果(annotation.annotationType().isAnnotationPresent(
允许使用角色(类){
RoleType[]RoleType=getRoleTypesFromAnnotation(annotation);
if(roleTypes!=null)
for(RoleType RoleType:RoleType)
roleTypesAllowed.add(roleType);
}
}
允许退货;
}
公共静态角色类型[]getRoleTypesFromAnnotation(注释注释){
方法[]方法=annotation.annotationType().getMethods();
用于(方法:方法){
String name=method.getName();
类returnType=method.getReturnType();
类componentType=returnType.getComponentType();
if(name.equals(“value”)&&returnType.isArray()
&&RoleType.class.isAssignableFrom(componentType)){
角色类型[]特征;
试一试{
features=(RoleType[])(method.invoke(annotation,
新对象[]{});
}捕获(例外e){
抛出新的运行时异常(
“在中执行value()方法时出错”
+annotation.getClass().getCanonicalName(),
e) );
}
返回特性;
}
}
抛出新的运行时异常(
“No value()方法返回RoleType[]类型”
+“在批注中找到”
+注释.getClass().getCanonicalName());
}
}
公共类角色列表{
@允许学术角色({DEANERY})
公共级迪纳雷德莫{
}
@试验
公共图书馆{
List roleTypes=RoleAllowedUtil.GetRoleTypeAllowedFromAnnotations(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) { ... }
一个小问题是,对于任何修改,我们需要在两个地方进行更改。但是因为它们在同一个文件中