Java 註解實戰

前言:Java 註解,對於不少人都不陌生了,可是在公司的實際開發中,可能讓咱們本身去定義註解並應用到生產環境中的機會比較少,因此會致使一部分人對註解的理解比較淺,在看到一些框架或者別人的代碼中有註解的代碼就會頭大,不知道這表明的是什麼意思。其實究其緣由,最主要的仍是咱們對註解的這個概念沒搞清楚,因此本文通篇會串插着這些重要的概念,從幾個主題入手,從淺到深的帶領你們進入註解的世界,讓你們在從此的代碼旅程中輕鬆應對註解。html

註解是 Java 5 的一個新特性。先給你們梳理一個在生產環境中註解的經常使用使用流程。第一步: 定義一個註解;第二步: 在類上面或方法上面等地方使用註解;第三步: 經過註解處理器獲取註解信息並作相應的處理(這個處理也就是註解真正起做用的地方了)。本篇文章的整個脈絡大體也是按照這三步來寫的。java

JDK 內置註解

先來看幾個 Java 內置的註解,讓你們熱熱身。express

  • @Override 演示
class Parent {
    public void run() {
    }
}

class Son extends Parent {
    /**
     * 這個註解是爲了檢查此方法是否真的是重寫父類的方法
     * 這時候就不用咱們用肉眼去觀察究竟是不是重寫了
     */
    @Override
    public void run() {
    }
}
  • @Deprecated 演示
class Parent {

    /**
     * 此註解表明過期了,可是若是能夠調用到,固然也能夠正常使用
     * 可是,此方法有可能在之後的版本升級中會被慢慢的淘汰
     * 能夠放在類,變量,方法上面都起做用
     */
    @Deprecated
    public void run() {
    }
}

public class JDKAnnotationDemo {
    public static void main(String[] args) {
        Parent parent = new Parent();
        parent.run(); // 在編譯器中此方法會顯示過期標誌
    }
}
  • @SuppressWarnings 演示
class Parent {

    // 由於定義的 name 沒有使用,那麼編譯器就會有警告,這時候使用此註解能夠屏蔽掉警告
    // 即任意不想看到的編譯時期的警告均可以用此註解屏蔽掉,可是不推薦,有警告的代碼最好仍是處理一下
    @SuppressWarnings("all")
    private String name;
}
  • @FunctionalInterface 演示
/**
 * 此註解是 Java8 提出的函數式接口,接口中只容許有一個抽象方法
 * 加上這個註解以後,類中多一個抽象方法或者少一個抽象方法都會報錯
 */
@FunctionalInterface
interface Func {
    void run();
}

定義一個註解

完整註解

這裏先提供一個比較完整的註解,即此註解內容基本包含了咱們在生產環境中經常使用的信息了,而後根據這個註解來介紹枚舉的重要概念。首先看到下面這個完整註解包含三部份內容,第一部分就是 @interface 來定義註解,全部註解隱式繼承 java.lang.annotation.Annotation;第二部分就是這個註解的內部信息;第三部分就是這個註解的頭部,即元註解。api

@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER, ElementType.CONSTRUCTOR})
@Retention(RetentionPolicy.RUNTIME)
@Inherited // 此註解應用的相對較少,本文很少作介紹,可自行研究
@Documented // 此註解應用的相對較少,本文很少作介紹,可自行研究
public @interface ApiAuthAnnotation {
    int age();

    String name() default "lyf";

    AnnotationDemo annotattion() default @AnnotationDemo;

    Season Sason() default Season.SPRING;

    int[] nums() default {1, 2, 3};

    Class clazz() default String.class;
}

註解內部信息

所謂註解內部信息,也就是這個註解裏面都寫什麼,經過如上的完整註解能夠很直觀的看到都寫了什麼,其實就是一種比較特殊的方法了,帶有返回值類型,沒有具體實現,後面能夠選填默認值。大概也就這麼點內容了。其支持的數據類型有:數組

  • 基本數據類型
  • java.lang.String
  • 註解
  • 枚舉
  • 一維數組
  • java.lang.Class

元註解

元註解是指用於註解類型上的註解。通俗來說,元註解的做用就是,規範咱們定義的註解,給這個註解一些規則。好比說: 我在註解頭部沒有寫元註解,那麼咱們自定義的這個註解在使用的時候放在類上面,方法上面,或者放在成員變量上面,沒有規則,想放哪裏就放哪裏;可是當咱們在註解頭部寫了元註解,並把元註解的 Target 設置爲 ElementType.METHOD,那麼咱們自定義的這個註解在使用的時候就只能放在方法上面,放在類上面或者成員變量上面就會報錯。瀏覽器

元註解的註解類型主要有以下幾種:app

  • @Target: 表示定義的註解的做用域,其值包括:框架

    • CONSTRUCTOR: 構造方法聲明;
    • FIELD: 屬性/字段聲明;
    • LOCAL_VARIABLE: 局部變量聲明;
    • METHOD: 方法聲明;
    • PACKAGE: 包聲明;
    • PARAMETER: 參數聲明;
    • TYPE: 類接口聲明;
    • ANNOTATION_TYPE: 註解類型聲明;
    • TYPE_PARAMETER: 類型參數聲明(jdk1.8 提供);
    • TYPE_USE: 類型使用(jdk1.8 提供)
  • @Retention: 自定義註解的生命週期,其值包括:ide

    • SOURCE: 只在源碼顯示,編譯時丟棄;(好比 jdk 自帶的 @SuppressWarnings 和 @Override)
    • CLASS: 編譯時記錄到.class中,運行時忽略;
    • RUNTIME: 運行時存在,可經過反射來讀取。(很重要,生產中咱們開發經常使用此值)
  • @Inherited: 表示註解是否可被子元素繼承。
  • @Documented: 表示 javadoc 是否爲實例產生文檔

注意事項

  • 註解不能繼承另外一個註解
  • 方法不能有參數
  • 方法不能拋出異常
  • 不能定義 Object 或 Annotation 接口中的方法(由於註解都隱式繼承 Annotation 接口)
  • 註解不能是泛型
  • 使用 default 指定屬性的默認值
  • 不能使用 null 做爲屬性的默認值

使用註解

這個固然就至關簡單了,哪一個地方須要使用咱們自定義的註解,那咱們在其地方添加上註解就 ok 了。以下代碼: 若是要做用到類,那就在類頭上加上註解;若是要做用到方法,那就在方法上加上註解。函數

@ApiAuthAnnotation(age = 16, name = "lyf")
@RestController
public class ApiController {

    @ApiAuthAnnotation(age = 20, name = "lisi")
    @RequestMapping(value = "/topic1", method = RequestMethod.GET)
    public void getByTopic1() {
        System.out.println("年齡大於 18 歲的能夠訪問此 API");
    }

    @ApiAuthAnnotation(age = 16, name = "zhangsan")
    @RequestMapping(value = "/topic2", method = RequestMethod.GET)
    public void getByTopic2() {
        System.out.println("年齡小於 18 歲的不能夠訪問此 API");
    }

}

從如上代碼來看,使用註解的時候則是至關的簡單了,咱們這裏是在類上和方法上都加上了自定義的註解了,至於這些註解加上之後怎麼起做用,那就跟註解處理器有關了。
【注: 在使用註解的時候其實還有不少小的語法技巧,不少時候能夠簡寫一些代碼,不過有沒有必要簡寫代碼,這個本身去衡量,這個不是重點,就是語法,能夠自行去研究下。就好比當註解只要一個元素,且其名字是 value 時,賦值的時候能夠簡寫】

註解處理器

註解處理器纔是使用註解整個流程中最重要的一步了。全部在代碼中出現的註解,它到底起了什麼做用,都是在註解處理器中定義好的。
概念:註解自己並不會對程序的編譯方式產生影響,而是註解處理器起的做用;註解處理器可以經過在運行時使用反射獲取在程序代碼中的使用的註解信息,從而實現一些額外功能。前提是咱們自定義的註解使用的是 RetentionPolicy.RUNTIME 修飾的。這也是咱們在開發中使用頻率很高的一種方式。

咱們先來了解下如何經過在運行時使用反射獲取在程序中的使用的註解信息。以下類註解和方法註解。

類註解

Class aClass = ApiController.class;
Annotation[] annotations = aClass.getAnnotations();

for(Annotation annotation : annotations) {
    if(annotation instanceof ApiAuthAnnotation) {
        ApiAuthAnnotation apiAuthAnnotation = (ApiAuthAnnotation) annotation;
        System.out.println("name: " + apiAuthAnnotation.name());
        System.out.println("age: " + apiAuthAnnotation.age());
    }
}

方法註解

Method method = ... //經過反射獲取方法對象
Annotation[] annotations = method.getDeclaredAnnotations();

for(Annotation annotation : annotations) {
    if(annotation instanceof ApiAuthAnnotation) {
        ApiAuthAnnotation apiAuthAnnotation = (ApiAuthAnnotation) annotation;
        System.out.println("name: " + apiAuthAnnotation.name());
        System.out.println("age: " + apiAuthAnnotation.age());
    }
}

此部份內容可參考: 經過反射獲取註解信息

註解處理器實戰

接下來我經過在公司中的一個實戰改編來演示一下註解處理器的真實使用場景。
需求: 網站後臺接口只能是年齡大於 18 歲的才能訪問,不然不能訪問
前置準備: 定義註解(這裏使用上文的完整註解),使用註解(這裏使用上文中使用註解的例子)
接下來要作的事情: 寫一個切面,攔截瀏覽器訪問帶註解的接口,取出註解信息,判斷年齡來肯定是否能夠繼續訪問。

在 dispatcher-servlet.xml 文件中定義 aop 切面

<aop:config>
    <!--定義切點,切的是咱們自定義的註解-->
    <aop:pointcut id="apiAuthAnnotation" expression="@annotation(cn.caijiajia.devops.aspect.ApiAuthAnnotation)"/>
    <!--定義切面,切點是 apiAuthAnnotation,切面類即註解處理器是 apiAuthAspect,主處理邏輯在方法名爲 auth 的方法中-->
    <aop:aspect ref="apiAuthAspect">
        <aop:around method="auth" pointcut-ref="apiAuthAnnotation"/>
    </aop:aspect>
</aop:config>

切面類處理邏輯即註解處理器代碼如

@Component("apiAuthAspect")
public class ApiAuthAspect {

    public Object auth(ProceedingJoinPoint pjp) throws Throwable {
        Method method = ((MethodSignature) pjp.getSignature()).getMethod();
        ApiAuthAnnotation apiAuthAnnotation = method.getAnnotation(ApiAuthAnnotation.class);
        Integer age = apiAuthAnnotation.age();
        if (age > 18) {
            return pjp.proceed();
        } else {
            throw new RuntimeException("你未滿18歲,禁止訪問");
        }
    }
}

總結

註解其實在咱們的代碼中用的也愈來愈多了,特別是框架裏面都會提供不少的註解,把註解的概念搞清楚以後,對於之後看別人的代碼和框架源碼都不少幫助,因此平時沒事也能夠多瞭解瞭解。好比咱們常用的 @Autowired 註解,其背後作了什麼事情,有興趣均可以去了解一下。再好比,權限框架 Shiro,裏面也提供不少註解來管理權限,他們是怎麼使用註解和處理註解的,均可以去研究下。有什麼問題,歡迎老鐵們一塊兒交流。。。

相關文章
相關標籤/搜索