Java中的@interface以及method.invoke()

来源:互联网 发布:课程顾问 知乎 编辑:程序博客网 时间:2024/05/29 13:00

前言

  这几天在看暑假要去实习的公司的框架源码,该框架十分轻量级,上手贼快。
  Web容器是Jetty,数据库是MySQL,使用该框架形式为插件开发,只需要按照该框架可以识别的规则开发,再将项目放到指定目录下,服务启动后加载该目录下的所有插件,用反射进行方法调用(几乎所有的框架底层都是用反射的吧)。
  看了一天过后对框架整体有了一定了解,但是之前自己没有写过相关的代码,因此写了几个小demo进行测试,今天谈一谈Java中的注解和反射。

method.invoke()

  反射的概念不再赘述,直接来说method.invoke()的使用。
  查看源代码如下:

@CallerSensitivepublic Object invoke(Object obj, Object... args)     throws IllegalAccessException, IllegalArgumentException,       InvocationTargetException { // Object...表示参数为0到多个    if (!override) {        if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {            Class<?> caller = Reflection.getCallerClass();            checkAccess(caller, clazz, obj, modifiers);        }    }    MethodAccessor ma = methodAccessor;             // read volatile    if (ma == null) {        ma = acquireMethodAccessor();    }    return ma.invoke(obj, args);}

  不深究源代码的意义,我们只来看参数,obj是该方法对应的类的对象,如果该方法是一个静态方法,obj参数可以为nullargs为该方法需要传入的参数,这里Object...表示参数为0到多个。如果要调用的方法没有参数,就传入参数。如果方法是void方法,那么返回null;如果方法返回类型是基本类型,返回基本类型的包装类。
  接下来来看demo:

Demo

测试的类为:

public class InvokeObject {    public InvokeObject() {        System.out.println("这是构造方法");    }    public void print() {        System.out.println("这是无参方法");    }    public void print(String str) {        System.out.println("这是有一个参数的方法,传入的参数为:" + str);    }    public void print(String str1, String str2) {        System.out.println("这是有一个参数的方法,传入的参数str1为:" + str1 + ",str2为:" + str2);    }}

  测试方法为:

import java.lang.reflect.Method;public class Main {    @SuppressWarnings("rawtypes")    public static void main(String[] args) throws Exception {        Class<InvokeObject> clazz = InvokeObject.class;        Method[] methods = clazz.getDeclaredMethods();        for (Method method : methods) {            // 方法的返回值类型            Class returnType = method.getReturnType();            System.out.print(returnType.getName() + " ");            // 方法的名字            System.out.print(method.getName() + " (");            // 获取参数类型            Class[] paramsType = method.getParameterTypes();            StringBuilder param = new StringBuilder();            for (Class class1 : paramsType) {                param.append(class1.getName() + ", ");            }            if (param.length() > 1) {                param.delete(param.length() - 2, param.length());            }            System.out.print(param.toString());            System.out.println(")");        }        Method method = clazz.getMethod("print");        method.invoke(null); // 注意这里穿的参数为null    }}

  执行的结果如下:

void print (java.lang.String, java.lang.String)void print (java.lang.String)void print ()Exception in thread "main" java.lang.NullPointerException    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)    at java.lang.reflect.Method.invoke(Method.java:498)    at cn.alone.Reflect.Main.main(Main.java:31)

  因为在invoke方法中传入了空参数,而且调用的方法不是静态方法,所以会报错,如果我们将无参的print方法设为static的或者传入一个InvokeObject对象,就会输出下面的结果:

void print (java.lang.String, java.lang.String)void print (java.lang.String)void print ()这是构造方法这是无参方法

  再次修改测试代码:

import java.lang.reflect.Method;public class Main {    @SuppressWarnings("rawtypes")    public static void main(String[] args) throws Exception {        Class<InvokeObject> clazz = InvokeObject.class;        Method[] methods = clazz.getDeclaredMethods();        for (Method method : methods) {            // 方法的返回值类型            Class returnType = method.getReturnType();            System.out.print(returnType.getName() + " ");            // 方法的名字            System.out.print(method.getName() + " (");            // 获取参数类型            Class[] paramsType = method.getParameterTypes();            StringBuilder param = new StringBuilder();            for (Class class1 : paramsType) {                param.append(class1.getName() + ", ");            }            if (param.length() > 1) {                param.delete(param.length() - 2, param.length());            }            System.out.print(param.toString());            System.out.println(")");        }        Method method = clazz.getMethod("print");        method.invoke(clazz.newInstance());        Method method1 = clazz.getMethod("print", String.class); // 获得参数为String,方法名为print的方法        method1.invoke(clazz.newInstance(), "一个参数");        Method method2 = clazz.getMethod("print", String.class, String.class);        method2.invoke(clazz.newInstance(), "第一个参数", "第二个参数");    }}

  执行结果如下:

void print (java.lang.String, java.lang.String)void print (java.lang.String)void print ()这是构造方法这是无参方法这是构造方法这是有一个参数的方法,传入的参数为:一个参数这是构造方法这是有一个参数的方法,传入的参数str1为:第一个参数,str2为:第二个参数

@interface注解

  Java中用@interface来定义一个注解Annotation,程序中使用注解也就是对注解的程序做了一个标记,之后就可以用反射来找到标记的程序,进而进行一些操作。
  我们熟悉的JDK自带的注解有@Override@Deprecated等,大名鼎鼎的Spring生态中各种注解让我们十分方便快捷地进行开发。

@Retention 元注解

  注解@Retention可以用来修饰注解,是注解的注解,称为元注解。查看源码(JDK1.8):

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

  继续查看RetentionPolicy的源码:

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}

  也就是说@Retention元注解需要传入一个参数value,有3种,分别为SOURCECLASSRUNTIME,他们的意义如下:

用@Retention(RetentionPolicy.CLASS)修饰的注解,表示注解的信息被保留在class文件(字节码文件)中当程序编译时,但不会被虚拟机读取在运行的时候;
用@Retention(RetentionPolicy.SOURCE )修饰的注解,表示注解的信息会被编译器抛弃,不会留在class文件中,注解的信息只会留在源文件中;
用@Retention(RetentionPolicy.RUNTIME )修饰的注解,表示注解的信息被保留在class文件(字节码文件)中当程序编译时,会被虚拟机保留在运行时。

Java注释@interface的用法

@Target元注解

  @Target也是一个元注解,这个元注解用来表明要标识的注解可以对什么进行注解,@Targetvalue有以下几种:

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}

  @Target(value)中的value可以有多个,如@Target({ElementType.FIELD, ElementType.METHOD})

Demo

  定义一个注解:

@Retention(RetentionPolicy.RUNTIME) // 注解的信息保存在class文件中public static @interface MyMethod {       // 注解内部可以指定注解属性}

  在InvokeObject的方法上加上注解:

public class InvokeObject {    public InvokeObject() {        System.out.println("这是构造方法");    }    @MyMethod    public void print() {        System.out.println("这是无参方法");    }    @MyMethod    public void print(String str) {        System.out.println("这是有一个参数的方法,传入的参数为:" + str);    }    public void print(String str1, String str2) {        System.out.println("这是有一个参数的方法,传入的参数str1为:" + str1 + ",str2为:" + str2);    }}

  Main方法用来测试:

import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.reflect.Method;import cn.alone.Reflect.InvokeObject;public class MyAnnotation {    @Retention(RetentionPolicy.RUNTIME) // 注解的信息保存在class文件中    public static @interface MyMethod {    }    public static void main(String[] args) throws NoSuchMethodException, SecurityException, Exception, Exception, Exception {        Method method = InvokeObject.class.getMethod("print"); // 获得方法名为print的无参方法        if (method.isAnnotationPresent(MyMethod.class)) {   // 如果是MyMethod注解标记的方法,返回true            method.invoke(new InvokeObject());        }    }}

  执行结果如下:

这是构造方法这是无参方法

  获取一个参数的方法:

public static void main(String[] args) throws NoSuchMethodException, SecurityException, Exception, Exception, Exception {    Method method = InvokeObject.class.getMethod("print", String.class); // 获得参数为String,方法名为print的方法    if (method.isAnnotationPresent(MyMethod.class)) {   // 如果是MyMethod注解标记的方法,返回true        method.invoke(new InvokeObject(), "一个参数");    }}

  执行结果如下:

这是构造方法这是有一个参数的方法,传入的参数为:一个参数

  如果获取3个参数的方法呢?

public static void main(String[] args) throws NoSuchMethodException, SecurityException, Exception, Exception, Exception {    Method method = InvokeObject.class.getMethod("print", String.class, String.class); // 获得参数为String,方法名为print的方法    if (method.isAnnotationPresent(MyMethod.class)) {   // 如果是MyMethod注解标记的方法,返回true        method.invoke(new InvokeObject(), "第一个参数", "第二个参数");    }}

执行结果什么都没有输出。

结语

  通过简单的Demo,了解的Java中注解的基本使用,以及反射调用类方法的基本用法。

0 0
原创粉丝点击