[改善Java代码]枚举和注解结合使用威力更大
注解的写法和接口很类似, 都采用了关键字 interface, 而且都不能有实现代码, 常量定义默认都是 pulbic static final 类型的.
他们的主要不同点是: 注解在 interface 前加上 @字符, 而且不能继承, 不能实现, 这经常会给我们的开发带来一些障碍.
分析一个 ACL(Access Contorl List , 访问控制列表) 设计案例.. 看看如何避免这些障碍.
ACL 中有三个重要的元素:
1. 资源, 有哪些信息是要被控制起来的.
2. 权限级别, 不同的访问者在规划在不同的级别中.
3. 控制器 (也叫鉴权人), 控制不同的级别访问不同的资源.
鉴权人是整个 ACL 的实际核心, 我们从最主要的鉴权人开始, 看代码:
//鉴权者接口 interface Identifier { //无权访问时的礼貌语 String REFUSE_WORD = "您无权访问"; // 鉴权 public boolean identify();}
这是一个鉴权人的接口, 定义了一个常量和一个鉴权方法, 接下来应该实现该鉴权方法, 但问题是我们的权限级别和鉴权方法之间是紧耦合的, 若分拆成两个类显得有点啰嗦, 怎么办? 我们可以直接定义一个枚举来实现.
//常用鉴权者 enum CommonIdentifier implements Identifier { //权限级别 Reader, Author, Admin;</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">实现鉴权</span> <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">boolean</span><span style="color: rgba(0, 0, 0, 1)"> identify() { </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">; }
}
定义了一个通用鉴权者, 使用的是枚举类型, 并且实现了鉴权者接口, 现在就剩下资源定义了, 这很容易定义, 资源就是我们写的类, 方法等, 之后再通过配置来决定哪些类, 方法允许什么级别的访问, 这里的问题是: 怎么把资源和权限级别关联起来呢?
使用 XML 配置文件? 是个方法, 但是对于我们的示例程序来说显得太过繁重, 使用注解会更简洁些. 需要首先定义出权限级别的注解, 代码如下:
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @interface Access { //确定什么级别可以访问 CommonIdentifier level() default CommonIdentifier.Admin; }
该注解是标注在类上面的, 并且会保留到运行期, 我们定义一个资源类, 代码如下:
//商业逻辑,默认访问权限是 Admin @Access(level = CommonIdentifier.Author) class Foo {}
Foo 类只能是作者级别的人的访问, 场景定义完毕, 看如何模拟 ACL 的实现... 看代码:
1 import java.lang.annotation.ElementType; 2 import java.lang.annotation.Retention; 3 import java.lang.annotation.RetentionPolicy; 4 import java.lang.annotation.Target; 5 6 public class Client { 7 public static void main(String[] args) { 8 //初始化商业逻辑 9 Foo b = new Foo(); 10 //获取注解 11 Access access = b.getClass().getAnnotation(Access.class); 12 //没有 Access 注解或者鉴权失败 13 if (access == null || !access.level().identify()) { 14 //没有 Access 注解或者鉴权失败 15 System.out.println(access.level().REFUSE_WORD); 16 } 17 18 } 19 20 } 21 22 //商业逻辑,默认访问权限是 Admin 23 @Access(level = CommonIdentifier.Author) 24 class Foo { 25 26 } 27 28 //鉴权者接口 29 interface Identifier { 30 //无权访问时的礼貌语 31 String REFUSE_WORD = "您无权访问"; 32 // 鉴权 33 public boolean identify(); 34 } 35 36 //常用鉴权者 37 enum CommonIdentifier implements Identifier { 38 //权限级别 39 Reader, Author, Admin; 40 41 //实现鉴权 42 public boolean identify() { 43 return false; 44 } 45 } 46 47 @Retention(RetentionPolicy.RUNTIME) 48 @Target(ElementType.TYPE) 49 @interface Access { 50 //确定什么级别可以访问 51 CommonIdentifier level() default CommonIdentifier.Admin; 52 }
打印输出:
您无权访问
这段代码, 简单, 易读, 而且是通过 ClassLoader 类来解释该注解的, 那会使开发更加简洁, 所有的开发人员只要增加注解即可以解决访问控制问题.
//======================================================================
1 import java.lang.annotation.ElementType; 2 import java.lang.annotation.Retention; 3 import java.lang.annotation.RetentionPolicy; 4 import java.lang.annotation.Target; 5 6 public class Client { 7 public static void main(String[] args) { 8 MyAnno an1 = Foo.class.getAnnotation(MyAnno.class); 9 MyAnno an2 = Impl.class.getAnnotation(MyAnno.class); 10 System.out.println(an1);//@cn.summerchill.test.MyAnno(desc=@cn.summerchill.test.MyAnno2(a=a1a)) 11 System.out.println(an2);//@cn.summerchill.test.MyAnno(desc=@cn.summerchill.test.MyAnno2(a=aa)) 12 System.out.println(an1.hashCode());//391187329 13 System.out.println(an2.hashCode());//391149008 14 System.out.println(an1.equals(an2));//false 15 System.out.println(an1 == an2);//false 16 } 17 } 18 19 20 @MyAnno(desc =@MyAnno2(a="a1a")) 21 class Foo { 22 public void doSomething(){ 23 24 } 25 } 26 @MyAnno(desc =@MyAnno2(a="aa")) 27 class Impl { 28 29 public void doSomething() { 30 } 31 } 32 33 34 @Retention(RetentionPolicy.RUNTIME) 35 @Target(ElementType.TYPE) 36 @interface MyAnno { 37 MyAnno2 desc(); 38 } 39 40 @interface MyAnno2{ 41 String a() default ""; 42 }