自定義注解類編寫的一些規則:
1. Annotation型定義為@interface, 所有的Annotation會自動繼承java.lang.Annotation這一接口,并且不能再去繼承別的類或是接口.
2. 參數成員只能用public或默認(default)這兩個訪問權修飾
3. 參數成員只能用基本類型byte,short,char,int,long,float,double,boolean八種基本數據類型和String、Enum、Class、annotations等數據類型,以及這一些類型的數組.
4. 要獲取類方法和字段的注解信息,必須通過Java的反射技術來獲取 Annotation對象,因為你除此之外沒有別的獲取注解對象的方法
5. 注解也可以沒有定義成員, 不過這樣注解就沒啥用了
自定義注解類時, 可以指定目標 (類、方法、字段, 構造函數等) , 注解的生命周期(運行時,class文件或者源碼中有效), 是否將注解包含在javadoc中及是否允許子類繼承父類中的注解, 具體如下:
1. @Target 表示該注解目標,可能的 ElemenetType 參數包括:
ElemenetType.CONSTRUCTOR 構造器聲明
ElemenetType.FIELD 域聲明(包括 enum 實例)
ElemenetType.LOCAL_VARIABLE 局部變量聲明
ElemenetType.METHOD 方法聲明
ElemenetType.PACKAGE 包聲明
ElemenetType.PARAMETER 參數聲明
ElemenetType.TYPE 類,接口(包括注解類型)或enum聲明
2.?@Retention 表示該注解的生命周期,可選的 RetentionPolicy 參數包括
RetentionPolicy.SOURCE 注解將被編譯器丟棄
RetentionPolicy.CLASS 注解在class文件中可用,但會被VM丟棄
RetentionPolicy.RUNTIME VM將在運行期也保留注釋,因此可以通過反射機制讀取注解的信息
3. @Documented 指示將此注解包含在 javadoc 中
4.??@Inherited 指示允許子類繼承父類中的注解
類注解的定義:
importjava.lang.annotation.ElementType;
importjava.lang.annotation.Retention;
importjava.lang.annotation.RetentionPolicy;
importjava.lang.annotation.Target;
/**
*?注解類
*?@author?Owner
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public@interfaceMyClassAnnotation?{
String?uri();
String?desc();
}
構造方法注解定義:
importjava.lang.annotation.ElementType;
importjava.lang.annotation.Retention;
importjava.lang.annotation.RetentionPolicy;
importjava.lang.annotation.Target;
/**
*?構造方法注解
*?@author?Owner
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.CONSTRUCTOR)
public@interfaceMyConstructorAnnotation?{
String?uri();
String?desc();
}
方法注解定義:
importjava.lang.annotation.ElementType;
importjava.lang.annotation.Retention;
importjava.lang.annotation.RetentionPolicy;
importjava.lang.annotation.Target;
/**
*?我的方法注解
*?@author?Owner
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public@interfaceMyMethodAnnotation?{
String?uri();
String?desc();
}
字段注解定義:
importjava.lang.annotation.ElementType;
importjava.lang.annotation.Retention;
importjava.lang.annotation.RetentionPolicy;
importjava.lang.annotation.Target;
/**
*?字段注解定義
*?@author?Owner
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public@interfaceMyFieldAnnotation?{
String?uri();
String?desc();
}
最后定義一個測試類
importjava.lang.reflect.Constructor;
importjava.lang.reflect.Field;
importjava.lang.reflect.Method;
@MyClassAnnotation(desc="The?class?name",?uri="com.annotation.MySample")
publicclassMyTest?{
@MyFieldAnnotation(desc="The?class?field",?uri="com.annotation.MySample#id")
privateintid;
@MyConstructorAnnotation(desc="The?class?constructor",?uri="com.annotation.MySample#MySample")
publicMyTest(){}
publicintgetId()?{
returnid;
}
@MyMethodAnnotation(desc="The?class?method",?uri="com.annotation.MySample#setId")
publicvoidsetId(intid)?{
this.id?=?id;
}
publicstaticvoidmain(String[]?args)throwsException?{
Class?clazz?=?MyTest.class;
//得到類注解
MyClassAnnotation?myClassAnnotation?=?clazz.getAnnotation(MyClassAnnotation.class);
System.out.println(myClassAnnotation.desc()+"?"+myClassAnnotation.uri());
//得到構造方法注解
Constructor?cons?=?clazz.getConstructor(newClass[]{});
MyConstructorAnnotation?myConstructorAnnotation?=?cons.getAnnotation(MyConstructorAnnotation.class);
System.out.println(myConstructorAnnotation.desc()+"?"+myConstructorAnnotation.uri());
//獲取方法注解
Method?method?=?clazz.getMethod("setId",newClass[]{int.class});
MyMethodAnnotation?myMethodAnnotation?=?method.getAnnotation(MyMethodAnnotation.class);
System.out.println(myMethodAnnotation.desc()+"?"+myMethodAnnotation.uri());
//獲取字段注解
Field?field?=?clazz.getDeclaredField("id");
MyFieldAnnotation?myFieldAnnotation?=?field.getAnnotation(MyFieldAnnotation.class);
System.out.println(myFieldAnnotation.desc()+"?"+myFieldAnnotation.uri()?);
}
}
輸出:
The class name com.annotation.MySample
The class constructor com.annotation.MySample#MySample
The class method com.annotation.MySample#setId
The class field com.annotation.MySample#id
好了,上面是基本學習,我們在實際的項目中用在什么地方呢?我想我們都做過關于細粒度權限攔截的問題,在Struts2中可以根據登錄用戶所具有的的權限進行任
@Retention(RetentionPolicy.RUNTIME)//代表Permission注解保留在的階段
@Target(ElementType.METHOD)//標注在方法上面
public@interfacePermission?{
/**?模塊?*/
String?module();
/**?權限值?*/
String?privilege();
}
比如有一個部門action,Department.action,有一個方法public String departmentlistUI(){}
可以這樣定義方法
@Permission(module="department",privilege="view")
publicString?departmentlistUI(){
}
然后自定定義一個權限攔截器PrivilegeInterceptor.java并在struts.xml中注冊,
在實現interceptor接口后,實現方法public String intercept(ActionInvocation invocation) throws Exception {}
在這里調用任一個action方法都會經過該攔截方法,通過invocation可以獲取當前調用的action的名字,以及調用的action的哪個方法,
通過這段代碼可以獲取action名字和方法名
String??actionName=invocation.getProxy().getActionName();
String??methodName=invocation.getProxy().getMethod();
System.out.println("攔截到:action的名字:"+actionName+"方法名:"+methodName);
然后通過反射技術,獲取該方法上的自定義權限注解,獲取當前登錄的用戶(從session中),遍歷當前用戶的所擁有的權限組,并且遍歷任一個權限組下的所有的權限,看是否包括該方法上注解所需的權限。這樣就可以完成細粒度的action方法權限攔截了。
這只是個大體的思路
下面看一下,攔截器的具體實現該功能的代碼
privatebooleanvalidate(ActionInvocation?invocation)throwsSecurityException,?NoSuchMethodException?{
String??methodName=invocation.getProxy().getMethod();
Method?currentMethod?=?invocation.getAction().getClass().getMethod(methodName);
if(currentMethod?!=null&&?currentMethod.isAnnotationPresent(Permission.class)){
//得到方法上的注解
Permission?permission?=?currentMethod.getAnnotation(Permission.class);
//該方法上的所需要的權限
SystemPrivilege?methodPrivilege?=?newSystemPrivilege(newSystemPrivilegePK(permission.module(),?permission.privilege()));
//得到當前登錄的用戶
Employee?e?=?(Employee)?ActionContext.getContext().getSession().get("loginUser");
//遍歷當前用戶下的所有的權限組
for(PrivilegeGroup?group?:?e.getGroups()){
//如果該權限組下包含,要訪問該方法所需要的權限,就放行
if(group.getPrivileges().contains(methodPrivilege)){
returntrue;
}
}
//說明遍歷的該用戶所有的權限組,沒有發現該權限,說明沒有該權限
returnfalse;
}
//沒有標注注解,表示誰都可以調用該方法
returntrue;
}