5分鐘搞懂java注解@Annotation的具體使用
首先一句話結論:注解就是一種通過在類、方法、或者屬性等上使用類似@xxx的方式進行“打標簽”,然后可以通過反射機制對標簽的內容進行解析并進行相應處理的手段。
注解是java中的一個重要知識點,從java5后開始引入,尤其在spring框架中大量使用。比較常用的有@controller、@service等等各種,本文將從注解的實現原理出發(fā),通過一些demo代碼的實現,進行分析。
一、 注解定義方式
直接上代碼,看看spring中@Service注解的定義就知道了:
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Service { String value() default ""; }
可以看到注解的定義和接口定義很像,但是多了@字符,注解的定義上有以下約定:
- 只能定義屬性名,不能定義方法
- 屬性的可見性只有public和default,不寫則默認后者
- 屬性的類型只能支持:基本數據類型、string、class、enum、Annotation類型及以上類型的數組
- 可以加上defult關鍵字指明默認值,當某字段不指明默認值時,必須在進行注解標注的時候進行此字段值的指定。
- 當使用value作為屬性名稱時,可以不顯式指定value=“xxx”,如可以直接使用@Service("xxxService")
二、元注解
所謂元注解就是java中默認實現的專門對注解進行注解的注解。元注解的總數就5個,下面我們以上面講到的@Service注解為例子各個擊破:
1.@Target
此注解用于表示當前注解的使用范圍,@Target({ElementType.TYPE})就代表著@Service這個注解是專門用來注解到類、接口、或者枚舉類型上面的,當在方法上面加這個注解時,就會報錯??梢钥吹阶⒔馕恢檬且粋€枚舉類型,完整定義如下
public enum ElementType { /** Class, interface (including annotation type), or enum declaration */ TYPE, /** Field declaration (includes enum constants) */ FIELD, /** Method declaration */ METHOD, /** Formal parameter declaration */ PARAMETER, /** Constructor declaration */ CONSTRUCTOR, /** Local variable declaration */ LOCAL_VARIABLE, /** Annotation type declaration */ ANNOTATION_TYPE, /** Package declaration */ PACKAGE, /** * Type parameter declaration * * @since 1.8 */ TYPE_PARAMETER, /** * Use of a type * * @since 1.8 */ TYPE_USE }
2.@Retention
此注解用于表示當前注解的生命周期,說人話就是這個注解作用會保留到什么時候,如@Retention(RetentionPolicy.RUNTIME)就表示在程序運行期間依然有效,此時就可以通過反射拿到注解的信息,完整的枚舉定義如下
public enum RetentionPolicy { /** * Annotations are to be discarded by the compiler. */ SOURCE, /** * Annotations are to be recorded in the class file by the compiler * but need not be retained by the VM at run time. This is the default * behavior. */ CLASS, /** * Annotations are to be recorded in the class file by the compiler and * retained by the VM at run time, so they may be read reflectively. * * @see java.lang.reflect.AnnotatedElement */ RUNTIME }
3.@Documented
當被此注解所注解時,使用javadoc工具生成文檔就會帶有注解信息。
4.@Inherited
此注解與繼承有關,當A注解添加此注解后,將A注解添加到某類上,此類的子類就會繼承A注解。
@Inherited public @interface A{ } @A public class Parent{} public class Son entends Parant{}//Son類繼承了父類的A注解
5.@Repeatable
此注解顧名思義是擁有可以重復注解的能力。想象這樣一個場景,我們需要定時執(zhí)行某個任務,需要在每周一和周三執(zhí)行,并且這個時間是可以靈活調整的,此時這個元注解就能派上用場:
@Repeatable(Schedules.class) public @interface Schedule { String date(); } public @interface Schedules { Schedule[] value(); } @Schedule(date = "周一") @Schedule(date = "周三") public class Executor { }
注意看到此元注解后面括號里內容,在這指定的類叫做容器注解,意思是保存這多個注解的容器,故我們創(chuàng)建一個@Schedules注解作為@Schedule的容器注解,容器注解必須含有一個名字為value,返回類型為需放入此容器的注解數組的屬性。
三、自定義實現一個注解
下面我們以web項目中非常常見的鑒權場景為例自己實現一個自定義注解。
首先我們定義系統的使用人員身份,有超級管理員、管理員、訪客三種身份。
public enum IdentityEnums { SUPER_ADMIN, ADMIN, VISIVOR }
接下來我們定義一個權限注解:
@Target({ElementType.TYPE,ElementType.METHOD}) @Documented @Retention(RetentionPolicy.RUNTIME) public @interface Authorization { IdentityEnums[] value(); }
然后使用攔截器的方式,對所有頁面進行統一的鑒權管理,此處只展示一些關鍵代碼:
public class AuthInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (handler instanceof HandlerMethod) { IdentityEnums user = getIdentityFromRequset(request);//這里從request里獲取賬號信息并判斷身份,自己實現 Authorization auth =((HandlerMethod) handler).getMethodAnnotation(Authorization.class);//獲取方法上面的注解 if (!Arrays.asList(auth.value()).contains(user)){ return false; } } return true; } }
最后在spring配置文件中對攔截器進行配置開啟攔截器:
<!-- 攔截器 --> <mvc:interceptors> <mvc:interceptor> <!-- 匹配的是url路徑, 如果不配置或/**,將攔截所有的Controller --> <mvc:mapping path="/**" /> <!-- 攔截器類 --> <bean class="com.xx.xx.AuthInterceptor"></bean> </mvc:interceptor> </mvc:interceptors>
在實際使用中,我們將在方法上面添加此自定義注解,當身份權限符合時,才能對頁面進行訪問,使用方式如下:
@ResponseBody
@RequestMapping(value = "/management") @Authorization({IdentityEnums.ADMIN,IdentityEnums.SUPER_ADMIN}) public String management(HttpServletRequest request, HttpServletResponse response) { log.info("has permission!"); }
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。