首先一句話結論:註解就是一種經過在類、方法、或者屬性等上使用相似@xxx的方式進行「打標籤」,而後能夠經過反射機制對標籤的內容進行解析並進行相應處理的手段。java
註解是java中的一個重要知識點,從java5後開始引入,尤爲在spring框架中大量使用。比較經常使用的有@controller、@service等等各類,本文將從註解的實現原理出發,經過一些demo代碼的實現,進行分析。web
直接上代碼,看看spring中@Service註解的定義就知道了:spring
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
String value() default "";
}
複製代碼
能夠看到註解的定義和接口定義很像,可是多了@字符,註解的定義上有如下約定:數組
所謂元註解就是java中默認實現的專門對註解進行註解的註解。元註解的總數就5個,下面咱們以上面講到的@Service註解爲例子各個擊破:bash
此註解用於表示當前註解的使用範圍,@Target({ElementType.TYPE})就表明着@Service這個註解是專門用來註解到類、接口、或者枚舉類型上面的,當在方法上面加這個註解時,就會報錯。能夠看到註解位置是一個枚舉類型,完整定義以下mvc
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
}
複製代碼
此註解用於表示當前註解的生命週期,說人話就是這個註解做用會保留到何時,如@Retention(RetentionPolicy.RUNTIME)就表示在程序運行期間依然有效,此時就能夠經過反射拿到註解的信息,完整的枚舉定義以下app
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
}
複製代碼
當被此註解所註解時,使用javadoc工具生成文檔就會帶有註解信息。框架
此註解與繼承有關,當A註解添加此註解後,將A註解添加到某類上,此類的子類就會繼承A註解。ide
@Inherited
public @interface A{
}
@A
public class Parent{}
public class Son extends Parent{}//Son類繼承了父類的A註解
複製代碼
此註解顧名思義是擁有能夠重複註解的能力。想象這樣一個場景,咱們須要定時執行某個任務,須要在每週一和週三執行,而且這個時間是能夠靈活調整的,此時這個元註解就能派上用場:工具
@Repeatable(Schedules.class)
public @interface Schedule {
String date();
}
public @interface Schedules {
Schedule[] value();
}
@Schedule(date = "週一")
@Schedule(date = "週三")
public class Executor {
}
複製代碼
注意看到此元註解後面括號裏內容,在這指定的類叫作容器註解,意思是保存這多個註解的容器,故咱們建立一個@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!");
}
複製代碼