java反射机制和自定义注解原理分析和实例

来源:互联网 发布:腾讯足球数据库 编辑:程序博客网 时间:2024/04/28 00:51

1、概述

熟悉android开发的都会了解到Android的xUtils框架的ViewUtils模块,简化了findViewById的编写,通过完全注解方式就可以进行UI,资源和事件绑定。实现基本原理就是通过java中的注解和反射实现,本文主要介绍java中的反射机制和自定义注解的原理和实例编写。

2、反射机制

定义:反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

(1)反射机制提供的主要功能:

  • 在运行时判断任意一个对象所属的类;
  • 在运行时构造任意一个类的对象;
  • 在运行时判断任意一个类所具有的成员变量和方法;
  • 在运行时调用任意一个对象的方法;
  • 生成动态代理。

3、反射机制应用案例

(1)获取某个类的属性

package com.chunsoft.testReflect;import java.lang.reflect.Field;import java.lang.reflect.Modifier;public class TestReflect{    private String testField1 = "testField1";    public int value = 2017;    public static void main(String[] args) throws Exception {        //获取当前类的字节码        Class<?> clazz = Class.forName("com.chunsoft.testReflect.TestReflect");         System.out.println("===============本类所有属性===============");         //获取本类的所有属性         Field[] declaredFields = clazz.getDeclaredFields();         for(int i = 0;i < declaredFields.length;i ++) {             //权限修饰符             int mo = declaredFields[i].getModifiers();             String priv = Modifier.toString(mo);             //属性类型             Class<?> type = declaredFields[i].getType();             String name = type.getName();             System.out.println(priv + " " +              name + " " + declaredFields[i].getName() + ";");         }         System.out.println("==========获取public的属性==========");        // 取得public的属性         Field[] fields = clazz.getFields();         for (int i = 0; i < fields.length; i++) {             //权限修饰符             int mod = fields[i].getModifiers();             String priv = Modifier.toString(mod);             //属性类型             Class<?> type = fields[i].getType();             String name = type.getName();             System.out.println(priv + " " +              name + " " + fields[i].getName() + ";");        }    }}

(2)获取某个类的全部方法

package com.chunsoft.testReflect;import java.lang.reflect.Method;import java.lang.reflect.Modifier;public class TestReflectMothod {    public static void main(String[] args) throws Exception {        Class<?> clazz = Class.forName("com.chunsoft.testReflect.TestReflectMothod");        //获取Public类型方法和实现的接口或者父类的方法        //Method method[] = clazz.getMethods();        //获取所有方法        Method method[] = clazz.getDeclaredMethods();        for (int i = 0; i < method.length; i++) {            //获取返回类型            Class<?> returnType = method[i].getReturnType();            //获取参数类型            Class<?>[] parameterTypes = method[i].getParameterTypes();            //获取权限修饰符            int temp = method[i].getModifiers();            System.out.print(Modifier.toString(temp) + " ");            System.out.print(returnType.getName() + "  ");            System.out.print(method[i].getName() + " ");            System.out.print("(");            for (int j = 0; j < parameterTypes.length; ++j) {                System.out.print(parameterTypes[j].getName() + " " + "arg" + j);                if (j < parameterTypes.length - 1) {                    System.out.print(",");                }            }            //获取异常类型            Class<?> exce[] = method[i].getExceptionTypes();            if (exce.length > 0) {                System.out.print(") throws ");                for (int k = 0; k < exce.length; ++k) {                    System.out.print(exce[k].getName() + " ");                    if (k < exce.length - 1) {                        System.out.print(",");                    }                }            } else {                System.out.print(")");            }            System.out.println();        }    }    private String testMethod(int age ,String name) {        return name+":"+age;    }}

(3)通过反射机制调用某个类的方法

package com.chunsoft.testReflect;import java.lang.reflect.Method;public class TestReflectUse {    public static void main(String[] args) throws Exception {        Class<?> clazz = Class.forName("com.chunsoft.testReflect.TestReflectUse");        // 调用TestReflectUse类中的reflect1方法        Method method = clazz.getMethod("reflect1");        method.invoke(clazz.newInstance());        // Java 反射机制 - 调用某个类的方法1.        // 调用TestReflect的reflect2方法        method = clazz.getMethod("reflect2", int.class, String.class);        method.invoke(clazz.newInstance(), 20, "张三");        // Java 反射机制 - 调用某个类的方法2.        // age -> 20. name -> 张三    }    public void reflect1() {        System.out.println("Java 反射机制 - 调用某个类的方法1.");    }    public void reflect2(int age, String name) {        System.out.println("Java 反射机制 - 调用某个类的方法2.");        System.out.println("age -> " + age + ". name -> " + name);    }}

更多用法示例

4、自定义注解

Java注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用。包含在java.lang.annotation 包中。

(1)注解的原理

JDK5.0中提供了注解的功能,允许开发者定义和使用自己的注解类型。该功能由一个定义注解类型的语法和描述一个注解声明的语法,读取注解的API,一个使用注解修饰的class文件和一个注解处理工具组成。

目前已被广泛应用于各种Java框架,如Hibernate,Jersey,Spring。注解相当于是一种嵌入在程序中的元数据,可以使用注解解析工具或编译器对其进行解析,也可以指定注解在编译期或运行期有效。

元注解是指注解的注解。包括 @Retention @Target @Document @Inherited四种。

(2)创建自定义注解

使用@interface自定义注解时,自动继承java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口。

  • 自定义注解:
public @interface MyAnnotation {}
  • 自定义注解使用
public class AnnotationTest {    @MyAnnotation    public void execute(){        System.out.println("method");    }}
  • 添加变量
public @interface MyAnnotation {    String value1();}

当注解中使用的属性名为value时,对其赋值时可以不指定属性的名称而直接写上属性值接口;除了value意外的变量名都需要使用name=value的方式赋值。

  • 自定义注解使用
public class AnnotationTest {    @MyAnnotation(value1="abc")    public void execute(){        System.out.println("method");    }}
  • @Retention注解可以在定义注解时为编译程序提供注解的保留策略。
    CLASS
    编译器将把注释记录在类文件中,但在运行时 VM 不需要保留注释。
    RUNTIME
    编译器将把注释记录在类文件中,在运行时 VM 将保留注释,因此可以反射性地读取。
    SOURCE
    编译器要丢弃的注释。

  • @Target – 表示支持注解的程序元素的种类,一些可能的值有TYPE, METHOD, CONSTRUCTOR, FIELD等。如果Target元注解不存在,那么该注解就可以使用在任何程序元素之上。

  • Java提供3种内置注解

    1. @Override – 当我们想要覆盖父类的一个方法时,需要使用该注解告知编译器我们正在覆盖一个方法。这样的话,当父类的方法被删除或修改了,编译器会提示错误信息。大家可以学习一下为什么我们总是应该在覆盖方法时使用Java覆盖注解。

    2. @Deprecated – 当我们想要让编译器知道一个方法已经被弃用(deprecate)时,应该使用这个注解。Java推荐在javadoc中提供信息,告知用户为什么这个方法被弃用了,以及替代方法是什么。

    3. @SuppressWarnings – 这个注解仅仅是告知编译器,忽略它们产生了特殊警告,比如:在java泛型中使用原始类型。它的保持性策略(retention policy)是SOURCE,在编译器中将被丢弃。

5、反射和自定义注解结合

大部分java框架的实现通过反射机制和自定义的结合,本文介绍反射和自定义注解的简单结合。

(1)实现自定义注解类

新建Annotation方法,ViewInject.class:

package com.chunsoft.AnnotationTest;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;//自定义注解类@Target(ElementType.FIELD) //用于限制当前自定义注解类的作用的对象//@Retention(RetentionPolicy.SOURCE) //该注解类只会在源码中出现,当将源码编译成注解码时,注解信息就会被清除//@Retention(RetentionPolicy.CLASS) //该注解类会被编译到注解码中,但是当虚拟机加载字节码时,注解信息会被清除@Retention(RetentionPolicy.RUNTIME) //该注解类,永远保留到被加载到虚拟机中public @interface ViewInject {    int age();    String name();}

(2)在实体类中使用注解

新建一个实体类并在属性上使用注解,User.class:

package com.chunsoft.AnnotationTest;public class User {    @ViewInject(age=21,name="chunsoft")    private String name;    private int age;    private String eat(String eat) {        System.out.println("eat:"+eat);        return eat + " 真好吃";    }    @Override    public String toString() {        return "User [name=" + name + ", age=" + age + "]";    }}

(3)反射和自定义注解测试

package com.chunsoft.AnnotationTest;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;public class AnnotationMainTest {    public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {        //需求:获取User类中name字段上的自定义注解的值,        //然后将该值的age通过反射设置给User对象的age属性,将name设置给User对象的name属性        User user = new User();        /**         * 1.获取User类的字节码         *///      user.getClass();//      User.class;//      Class.forName("");        Class clazz= User.class;        /**         * 2.将字节码中的name字段获取到         *///      clazz.getField("");//只能获取声明为public的字段        Field declaredField = clazz.getDeclaredField("name");        Field declaredFieldAge = clazz.getDeclaredField("age");        /**         * 3.将当前字段上的注解对象获取到         */        ViewInject viewInject =  declaredField.getAnnotation(ViewInject.class);        if (viewInject != null) {            /**             * 4.获取自定义注解对象的参数             */            int age = viewInject.age();            String name = viewInject.name();            System.out.println("name="+name+",age="+age);            /**             * 5.通过反射将这两个值反射给User对象             */            declaredField.setAccessible(true); //设置允许访问,其实就是允许暴力反射            declaredFieldAge.setAccessible(true);            //将user对象的declaredField设置为name            declaredField.set(user, name);            declaredFieldAge.set(user, age);            System.out.println(user.toString());        }else {            System.out.println("字段上面没有自定义注解");        }        //通过反射调用eat对象的方法        Method declaredMethod = clazz.getDeclaredMethod("eat", String.class);        //设置允许访问        declaredMethod.setAccessible(true);        //暴力反射调用该方法        Object result = declaredMethod.invoke(user, "牛肉拉面");        System.out.println(result);    }}

本文简单介绍了java反射机制和自定义注解原理分析和实例,下篇文章将实现基本的ViewUtils基本功能,减少重复编码。

0 0