Java reflection and annotations

reflection

In the running state, for any class, you can know all the properties and methods of this class; for any object, you can call any of its methods and properties; this function of dynamically obtaining information and dynamically calling object methods is called Java language reflection mechanism

1. Obtain class information through Class

In Java, everything is an object. For example, we define a user class User, and then use it to instantiate an object u1. In fact, the User class itself can also be seen as an object, as an instance of the java.lang.Class class. You can obtain the class User in the following three ways:

        //通过类名的静态成员属性class
        Class c1=User.class;
        //通过对象方法getClass()
        User u1=new User();
        Class c2=u1.getClass();
        //通过静态加载类
        Class c3=null;
        try {
            c3=Class.forName("modules.User");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        //以上获取的三个类均为Student
        System.out.println(c1 == c2);        //输出true
        System.out.println(c2 == c3);        //输出true
        //通过c1创建User对象
        try {
            User u2=(User)c1.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

The Class class provides many methods to obtain the information of its "object", that is, the class in Java, such as the name of the class, member variables, method return values, parameters, etc. You can get the class through getClass (), and then getXxx () Get the attributes of the class, for example, get member variables through getField () and getMethods () to get detailed information about these attributes

    public static void printClassMessage(Object obj) {
        //获取对象类的类型
        Class c = obj.getClass();
        //打印类的名称
        System.out.println("类名称:" + c.getName());

        //打印类成员变量的信息
        System.out.println("类包含的成员变量如下:");
//        Field[] variables=c.getFields();        //获取所有public变量
        Field[] variables = c.getDeclaredFields();        //获取所有变量
        for (Field var : variables) {
            Class varType = var.getType();        //得到成员变量的类型
            String typeName = varType.getName();
            String varName = var.getName();       //获取成员变量名
            System.out.println(typeName + " " + varName);
        }

        //打印类构造函数信息
        System.out.println("类构造方法如下:");
        Constructor[] constructors = c.getConstructors();
        for (Constructor constructor : constructors) {
            System.out.print(constructor.getName() + " (");        //构造方法名
            Class[] params = constructor.getParameterTypes();     //获取构造方法参数的类型
            for (Class param : params)
                System.out.print(param.getName() + ",");
            System.out.println(")");
        }

        //打印类方法的信息
        System.out.println("类包含的方法如下:");
        Method[] methods = c.getMethods();          //获取类的所有public方法,包括从父类继承的
//        Method[] methods=c.getDeclaredMethods();        //获取类自定义的所有方法,不包括父类
        for (Method method : methods) {
            //获取方法返回值类型
            Class returnType = method.getReturnType();
            System.out.print(returnType.getName() + " ");
            //获取方法名
            System.out.print(method.getName() + " (");
            //获取方法的所有参数的类型
            Class[] parameterTypes = method.getParameterTypes();
            for (Class parameter : parameterTypes) {
                System.out.print(parameter.getName() + ",");
            }
            System.out.println(")");
        }
    }



//-----------------测试打印int类的信息----------    
        int i=1;
        printClassMessage(i);

//---------------输出结果--------------------
类名称:java.lang.Integer
类包含的成员变量如下:
int MIN_VALUE
int MAX_VALUE
java.lang.Class TYPE
......
类构造方法如下:
java.lang.Integer (int,)
java.lang.Integer (java.lang.String,)
类包含的方法如下:
int numberOfLeadingZeros (int,)
int numberOfTrailingZeros (int,)
int bitCount (int,)
......

Classes can be dynamically loaded through Class.forName () . The so-called dynamic loading means that only the required classes are loaded at runtime. The opposite is static loading . Generally, Java .class files are loaded statically during the compilation phase, that is, compiling all the files related to it. If there is an error, the compilation will fail.

For example, defining two classes ClassA and ClassB both implement the run () method in the CommonClass interface. By dynamically loading and passing in different class names, you can create different classes and execute the run () method to achieve the same code creation. Class object.

//定义公共接口
public interface CommonClass {
    public void run();
}

//定义A类
public class ClassA implements CommonClass {
    @Override
    public void run() {
        System.out.println("这是A类对象");
    }
}

//定义B类
public class ClassB implements CommonClass {
    @Override
    public void run() {
        System.out.println("这是B类对象");
    }
}

//在主程序中调用
        try {
            Class o1=Class.forName("modules.ClassA");        //动态加载ClassA          
            CommonClass c=(CommonClass) o1.newInstance();
            c.run();                                    //输出这是A类对象
           
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }

2. Reflection operation

In Java, the method is generally called through the object, that is, obj.method (params ...), and the reflection operation refers to the method to operate the object method.invoke (obj, params ...), so as to obtain the class at runtime Information to achieve the effect of dynamic coding. First get the class of the object through getClass, and then get the specific method through getMethod ("method", paramsType ...), method is the method name, paramsType is the method parameter type, so that a specific method can be uniquely determined by the method name and parameter. After that, you can call the method through invoke. If the function has a return value, it will return an Object object. You need to manually cast it to the desired type.

For example, define class ClassA and call the add method of its object through reflection

public class ClassA {

    public int add(int a, int b) {
        return a + b;
    }
}

        ClassA a=new ClassA();
        Class c=a.getClass();       //获取类
        try {
            Method m=c.getMethod("add", int.class, int.class);  //获取指定类方法add
            Object o=m.invoke(a,10,20);             //通过反射调用对象a的add方法
            int res=(Integer)o;                 //返回结果为Object,需要类型转换
            System.out.println("结果为:"+res);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

By reflection, you can bypass compilation and call the method of the object during the run phase. For example, when using ArrayList, you can specify that the generic Integer indicates that this list can only store integer data, but this rule is only checked at the compilation stage, and if there are other types of data, an error will be reported. However, you can add other types of data during the run phase by calling the list's add () method. Finally, the content of the output list shows int data 10 and String strings.

        ArrayList<Integer> list = new ArrayList<Integer>();
        list.add(10);

        Class c = list.getClass();
        try {
            Method m = c.getMethod("add", Object.class);
            m.invoke(list, "字符串");        //通过反射调用add添加String类型的数据
            System.out.println(list);               //输出:[10, 字符串]
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

annotation

Java annotations provide a way for element related information and data, and are widely used in various frameworks to make the code more concise and clear.

JDK provides three types of annotations @Override, @Deprecated, and @SuppressWarnings. @Override is used to override the parent class or implement the interface method in the subclass. If the method added by @Override does not have a corresponding method name in the parent class, an error will be reported. If a method in the parent class is no longer used, but cannot be deleted, because the subclass may still use this method, then you can add @Deprecated to the method of the parent class, which means that this method is deprecated, the subclass is calling this The method displays a warning with underline. However, if the subclass insists on using this method and ensures that it is available, you can add @SuppressWarnings ("deprecation") to the subclass method to ignore the warning.

The annotations according to the operating mechanism can be divided into the following three categories:

  1. Source code annotation: only exists in the source code, and disappears after being compiled into .class
  2. Annotation at compile time: It still exists in the .class file and works at compile time. For example, three annotations of JDK
  3. Annotation at runtime: still works during the operation phase, affecting the operation result

Custom annotation

An annotation is defined by the keyword @interface. Its member variables are declared in a parameter-free and exception-free manner, and default values ​​can be specified through default. Note that member types can only be basic types (such as int) and String, Class, Annotation, Enumeration , And then use "=" to assign values ​​to these variables when in use. If there is only one member variable, it is declared as value, and the incoming parameter is assigned automatically without using =. Annotations are called identification annotations if there are no members.

Meta annotations are used to set annotations when customizing annotations.

  1. @Target, define the target of the annotation function, you can scope constructor (Field), field declaration (Field), parameter declaration (Parameter), local variable (Local Variable), method (Method), class (Type), package (Package )
  2. @Retention defines the life cycle of annotations, which are source annotations (Source), compilation annotations (Class), and runtime annotations (Runtime)
  3. @Inherited, identifying annotations, annotations representing parent classes can be inherited by subclasses
  4. @Documented, identifies the annotation, which means that the annotation will be added to javadoc

Through the class or method getAnnotation (), the parameter information in the annotation can be obtained when the program is running, thereby realizing dynamic programming

The following is a custom annotation for the User class, and the information in the annotation is obtained at runtime:

@Target({ElementType.METHOD,ElementType.TYPE})  //注解作用于方法和类
@Retention(RetentionPolicy.RUNTIME)             //运行时注解
@Inherited                                      //可被继承
@Documented                                     //生成javadoc会包含
public @interface FirstAnnotation {
    String description();
    int value() default 0;
}



@FirstAnnotation(description = "这是User类注解", value = 8)
public class User {
    private String username;

    public User() {
    }

    @FirstAnnotation(description = "这是show()方法注解")
    public void show(){
        System.out.println("这是一个User对象");
    }

    public static void main(String[] args) {
        User u=new User();
        Class c=u.getClass();
        boolean isExist=c.isAnnotationPresent(FirstAnnotation.class);   //判断注解是否存在
        if (isExist){
            //拿到类的注解
            FirstAnnotation annotation=(FirstAnnotation) c.getAnnotation(FirstAnnotation.class);
            //输出类注解的属性description的值:这是User类注解
            System.out.println(annotation.description());
        }

        Method[] methods=c.getMethods();
        for (Method m:methods){
            boolean mExist=m.isAnnotationPresent(FirstAnnotation.class);
            if (mExist){
                //获取方法的注解
                FirstAnnotation mAnnotation=(FirstAnnotation) m.getAnnotation(FirstAnnotation.class);
                //输出方法注解的属性description的值:这是show()方法注解
                System.out.println(mAnnotation.description());
            }
        }
    }
}

3. An example

For example, the values ​​of different tables and fields in the database are queried through reflection and annotations. Define two annotations @Table and @Column, the value in them respectively saves the table name and field value of a class in the database.

For example, the Student class has three variables id, name, and age corresponding to the id, Name, and Age fields of the student table in the database, and then the table name and field name are stored in the annotation. Later, when the program is running, a student object is created, from which the student variable value and the corresponding field name in the annotation are taken out, which can be spliced ​​into a query statement, and then the database is queried to obtain the object information. The variable id is 1001, its annotation Column is id, age is 23, its annotation Column is Age, and the stitched query statement is: SELECT * FROM student WHERE 1 = 1 AND id = 1001 AND Age = 23.

By using annotations, when querying other objects, you can add different annotations to get the same result without rewriting a query method. For example, there is a new User object, corresponding to the database user table, the fields are username and password, you only need to add @Table ("user") when creating the User class, and add annotations to its variables @Column ("username") , @Column ("password"), that ’s all.

//定义Table注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
    String value();
}

//定义Column注解
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
    String value();
}

//------------------在Student类定义时使用注解-------------
@Table("student")
public class Student {
    @Column("id")
    private int id;

    @Column("Name")
    private String name;

    @Column("Age")
    private int age;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}


//------------在主函数调用执行查询----------------
    public static void main(String[] args) {
        Student student = new Student();
        student.setId(1001);
        student.setAge(23);
        queryTable(student);
    }

    //定义查询方法,通过不同的传入对象拼接查询语句
    public static void queryTable(Student student) {
        StringBuffer sql = new StringBuffer();
        sql.append("SELECT * FROM ");
        Class c = student.getClass();

        //通过Table注解获得要查询的表名
        Table t = (Table) c.getAnnotation(Table.class);
        String tableName = t.value();
        sql.append(tableName).append(" WHERE 1=1");

        //遍历类所有字段获取Column注解的内容并拼接成查询语句
        Field[] fields = c.getDeclaredFields();
        for (Field field : fields) {
            Column column = field.getAnnotation(Column.class);
            String columnName = column.value();           //从Column获取查询的字段
            //拼接字段的get方法,例如要获取name值,则将其首字母大写并在前面加上get,即getName
            String fieldName = field.getName();
            String methodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
            Object fieldValue = null;
            try {
                //调用字段的get方法获取该字段的值
                Method getMethod = c.getMethod(methodName);
                fieldValue = getMethod.invoke(student);
            } catch (Exception e) {
                e.printStackTrace();
            }
            if (fieldValue != null)
                sql.append(" AND ").append(columnName).append(" = ").append(fieldValue);
        }
        System.out.println(sql);
    }

 

Published 124 original articles · Like 65 · Visit 130,000+

Guess you like

Origin blog.csdn.net/theVicTory/article/details/104426942