Java 将枚举类型用作@RoleAllowed批注的值参数
我正在开发一个Java企业应用程序,目前正在进行JavaEE安全性工作,以限制特定用户对特定功能的访问。我配置了应用服务器和所有东西,现在我使用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")
@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) { ... }
一个小问题是,对于任何修改,我们需要在两个地方进行更改。但是因为它们在同一个文件中