定義
註解是在JDK1.5之後引入的新特性位於java.lang.annotation,註解其實就是對代碼進行一種特殊的標記,這些標記可以在編譯,類加載和運行時被讀取,並執行相應的處理。
第三方註解
在Java開發者,JDK自帶了一些註解,在第三方框架Spring 帶了大量的註解,這些註解稱爲第三方註解
1、Jdk通用註解
- @Override註解:編譯檢查,告訴編譯器這個是個覆蓋父類的方法。如果父類刪除了該方法,則子類會報錯。
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
- @Deprecated 註解:編譯檢查,表示被註解的元素已被棄用
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.LOCAL_VARIABLE, ElementType.METHOD,
ElementType.PACKAGE, ElementType.PARAMETER, ElementType.TYPE})
public @interface Deprecated {
}
上面兩個註解除了都有一些元註解之外,沒有任何的東西,但是我們加了這兩個註解後,編譯器就能夠識別,可以將其理解爲是一個標籤,它並沒有實際的邏輯處理,而實現邏輯的就是註解的用戶。它本質就是一個 『標記式註解』,僅被編譯器可知 ,這就是註解的工作原理。
註解的本質就是一個接口,該接口默認繼承了java.lang.annotation.Annotation接口。
public interface Annotation {
boolean equals(Object var1);
int hashCode();
String toString();
Class<? extends Annotation> annotationType();
}
2、元註解
元註解用於註解其他註解的。Java 5.0定義了4個標準的元註解,如下:
- @Target
- @Retention
- @Documented
- @Inherited
2.1@Target
@Target註解用於聲明註解的作用範圍,例如作用範圍爲類、接口、方法等。源碼:
package java.lang.annotation;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.ANNOTATION_TYPE})
public @interface Target {
ElementType[] value();
}
其中ElementType表明了該註解可以使用的位置:
public enum ElementType {
TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE, TYPE_PARAMETER, TYPE_USE;
}
具體解釋:
枚舉 | 作用 |
ElementType.PACKAGE | 註解用在包 |
ElementType.TYPE | 註解作用於類型(類,接口,註解,枚舉) |
ElementType.ANNOTATION_TYPE | 註解作用於註解 |
ElementType.CONSTRUCTOR | 註解作用於構造方法 |
ElementType.METHOD | 註解作用於方法 |
ElementType.PARAMETER | 註解作用於方法參數 |
ElementType.FIELD | 註解作用於屬性 |
ElementType.LOCAL_VARIABLE | 註解作用於局部變量 |
2.2@Retention
@Retention註解的作用就是指定註解的生命週期。比如在編譯時可以處理運行時可以處理等。源碼:
@Documented
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = {ElementType.ANNOTATION_TYPE})
public @interface Retention {
public RetentionPolicy value();
}
它的枚舉類爲RetentionPolicy,只有三個可選值
枚舉 | 作用 |
RetentionPolicy.SOURCE | 源碼中保留,編譯期可以處理 |
RetentionPolicy.CLASS | Class文件中保留,Class加載時可以處理 |
RetentionPolicy.RUNTIME | 運行時保留,運行中可以處理 |
2.3@Documented
是一個標記註解,有該註解的註解會在生成 java 文檔中保留
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.ANNOTATION_TYPE})
public @interface Documented {
}
2.4@Inherited
@Inherited 註解修飾的註解時具有可繼承性的,就是說我們用 @Inherited 修飾了一個類,那麼這個類的子類也會默認繼承此註解。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.ANNOTATION_TYPE})
public @interface Inherited {
}
自定義註解
在第三方註解沒法滿足我們業務要求的時候,需要我們自己寫註解,這個時候就是自定義註解。
1.1、自定義註解語法
在註解中,需要使用四種元註解來聲明註解的作用範圍、生命週期、繼承,是否生成文檔等。另外在註解中也可以有自己的成員變量,如果一個註解沒有成員變量則稱爲標記註解。
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD })
@Documented
public @interface CxmAction {
String code() default "";// 權限碼
String descrption() default "";// 描述
String prompt() default ""; // 權限提示
}
1.2、註解使用說明
1.2.1、AOP + 註解
使用AOP是一種常見的處理方式,首選,需要定義切面類
@Aspect
@Component
@Slf4j
@Order(-1)
public class CxmLogbackAspect {
/***
* 定義controller切入點攔截規則,攔截SystemLog註解的業務方法
*/
@Pointcut("@annotation(com.cxm.common.annotation.SystemLog)")
public void logPointcut(){
}
/**
* 環繞
* @param proceedingJoinPoint
* @return
* @throws Throwable
*/
@Around("logPointcut()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
// 執行具體的業務邏輯
}
}
- 1.2.2、HandlerInterceptor + 註解
使用Spring的攔截器HandlerInterceptorAdapter,也可以實現註解的業務,比如系統中使用這種方式配合SkipCertification,實現了某些指定接口跳過token驗證的功能
@Slf4j
@Component
public class PermissionInterceptor extends HandlerInterceptorAdapter {
/**
*
* @Title: preHandle
* @Description: 在Controller執行之前調用,如果返回false,controller不執行
* @param @param request
* @param @param response
* @param @param handler
* @param @return
* @param @throws Exception
* @throws
*
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (!(handler instanceof HandlerMethod)) {// 如果不是HandlerMethod而是靜態資源的請求,就直接跳過去
return true;
}
HandlerMethod method = (HandlerMethod) handler;
SkipCertification certification = method.getMethodAnnotation(SkipCertification.class);
if (null != certification) {// 跳過token驗證
return true;
} else {
// 執行校驗
}
}
}
1.2.3、java反射 + 註解
通過反射,可以獲取添加註解的屬性值
public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {
TestAnnotation testAnnotation = new TestAnnotation();
Field[] fields = obj.getClass().getDeclaredFields();
for(Field field : fields) {
field.setAccessible(true);
if(field.isAnnotationPresent(testAnnotation )) {
map.put(field.getName(),field.get(obj));
}
}
}
1.2.4、 Filter + 註解
使用過濾器也可以實現直接的功能,比如利用fastJSON的AfterFilter,實現屬性增強
public void doWrite(List<Field> fields, Object object) {
for (int i = 0; i < fields.size(); i++) {
Field field = fields.get(i);
Stall stallAnn = AnnotationUtils.getAnnotation(field, Stall.class);
if (stallAnn != null) {
Object value = ReflectionUtils.getField(field, object);
if (value != null) {
this.doWrite(field, value);
}
}
}
}