Java之invoke與方法參數註解

  • 2019 年 10 月 4 日
  • 筆記

上一節中我們說了Java之中非常主要的一個實例—註解的入門,這節我們結合反射中的invoke動態調用看看註解的作用域問題

繼承是Java的一個重要特性而覆寫是Java繼承中不可獲取的,而編輯器對於覆寫的地方會只能添加Override對於這些大家都是習以為常了。但是這個是必須的嗎?NO……去掉Override自己手動編譯Java程序的時候依然沒問題,這是為什麼?我們先看看啥么是Override

@Target(ElementType.METHOD)  @Retention(RetentionPolicy.SOURCE)  public @interface Override {  }

Java源碼中這麼解釋:1 Override只是說明告訴開發者這個方法是被覆寫啦,並未功能性的含義。還有這個是給編輯器看的也就是做idea開發人員需要開發的功能

看了這些對我們有什麼用呢?這個是為了引入今天註解是有它的作用環境和作用域的引子,我們來看看上栗註解的構成

@Target 代表這個註解標註的目標(往下看可以看出這個也是個註解)

@Documented  @Retention(RetentionPolicy.RUNTIME)  @Target(ElementType.ANNOTATION_TYPE)  public @interface Target {      /**       * Returns an array of the kinds of elements an annotation type       * can be applied to.       * @return an array of the kinds of elements an annotation type       * can be applied to       */      ElementType[] value();  }

Target自身也是個註解,它的內部返回的是個數組枚舉:以此來獲取這個註解實用的對象—TYPE ,FIELD,METHD ……

@Retention

@Documented  @Retention(RetentionPolicy.RUNTIME)  @Target(ElementType.ANNOTATION_TYPE)  public @interface Retention {      /**       * Returns the retention policy.       * @return the retention policy       */      RetentionPolicy value();  }  

這個代表註解在Java編譯運行是這個註解所處的位置和運行時怎麼使用,也就是說是在源碼文件中還是在編譯後的文件中甚至是動態獲取

綜合就是—Target在代碼層次確定目標目標位置 Retention面向底層確定底層怎麼使用

理論一大堆,我們結合之前一塊看看怎麼使用

先看代碼後解讀

 void run(@AnimalBean(animal = "com.company.Duck") Animal animal) {        System.out.println("run before");          animal.move();          System.out.println("run after");      }

這次我們把註解放置到方法的參數位置—參數註解,對應的我們需要修改我們的註解讓其可以在runtime時使用,它修飾目標為方法參數

  @Retention(RetentionPolicy.RUNTIME)  @Target({ElementType.PARAMETER,ElementType.TYPE, ElementType.METHOD})  @interface AnimalBean {      String animal();  }

常規調用方式是new一個對象然後調用方法,但是這樣我們就沒法使用註解啦。那麼我們怎麼使用呢?其實這個時候需要的就是另一個方式invoke動態調用

 public Object invoke(Object obj, Object... args)          throws IllegalAccessException, IllegalArgumentException,             InvocationTargetException{             …………             }

在Java的層面Class實例可以動態創建,方法可以動態綁定創建

 Object strInstance = Class.forName("java.lang.String").newInstance();      Method strMethod = strInstance.getClass().getDeclaredMethod("toString");         strMethod.invoke(strInstance);

當然了這裡看不到數據輸出–沒有賦值,那麼看看如何使用這個樣的特性實現我們的run方法呢?

 AnimalFactory factory = new AnimalFactory();            Method method = factory.getClass().getDeclaredMethod("run", Animal.class);          Parameter[] parameters = method.getParameters();          for (int i = 0; i < parameters.length; i++) {              Parameter parameter = parameters[i];              if (parameter.isAnnotationPresent(AnimalBean.class)) {                  AnimalBean animalBean = parameter.getAnnotation(AnimalBean.class);                  String animalName = animalBean.animal();                  Animal animalObj = (Animal) Class.forName(animalName).newInstance();                  Object result = method.invoke(factory, animalObj);                  System.out.println(result);              }          }

我們創建了實例AnimalFactory factory = new AnimalFactory(),然後獲取Class和Method和然後根據參數的註解動態創建出了Animal,最後invoke調用

 Object result = method.invoke(factory, animalObj);

實現了通過註解完成參數的動態解析,當然這個是內部基本的實現邏輯思維,現實中我們不會一步步這麼寫,我們會採用Spring框架實現……