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框架实现……