Java Basics - Reflection

Keywords: Java

concept

Java reflection mechanism is to know all the properties and methods of any class in the running state; For any object, you can call any of its methods and properties; This kind of dynamically obtained information and the function of dynamically calling object methods are called the reflection mechanism of Java language.

How to understand reflection? A simple one sentence explanation reverses the traditional development ideas.

The traditional way is to create objects through classes: Class -- > objects.

Reflection is to reverse this process and get the class through the object: object -- > class.

How to represent the class obtained through the object?

It is represented by Class. This Class is the source of Java reflection and is used to describe other classes. Each instantiated object of Class is a description of other classes.

The following methods are defined in the Object class, which will be inherited by all subclasses:

public final Class getClass().

In other words, each Class can call the getClass() method to obtain the corresponding Class object to describe the target Class. We call this Class the runtime Class of the target Class.

Class<?> cls = Class.forName("com.itcast.***");

What can I do with a Class object?

Call the newInstance() method of the Class object to dynamically create the object of the target Class.

Class<?> cls = Class.forName("com.itcast.***");
ClazzTest clazzTest = (ClazzTest)cls.newInstance();

requirement:

1) The target class must have a parameterless constructor.

2) External methods have sufficient permissions to access the constructor of the target class.

In addition to dynamically creating the object of the target class, reflection can also dynamically call various methods of the object and access member variables

Java reflection mechanism mainly provides the following purposes:

  1. Determine the class of any object at run time

  2. Construct an object of any class at run time

  3. Judge the member variables and methods of any class at run time

  4. Call the method of any object at run time

Main API s related to reflection

java.lang.Class: describes a class.

Class<?> clazz = MethodTest.class;
System.out.println(clazz.getName());

output
com.itcast.MethodTest

java.lang.reflect.Method: describes the method of the class.

Class<?> clazz = MethodTest.class;
Method[] methods = clazz.getMethods();
for (Method method : methods) {
    System.out.println(method.getName());
}

java.lang.reflect.Field: describes the member variables of the class.

java.lang.reflect.Constructor: describes the construction method of the class.

Class is used to describe the structure of the target class, which is called the runtime class of the target class.

Common methods of Class:

public Class<?>[] getInterfaces()   Returns all interfaces implemented by the runtime class.
public Class<? Super T> getSuperclass()Returns the parent class of the runtime class.
public Constructor<T>[] getConstructors()Returns the public constructor of the runtime class.
public Constructor<T>[] getDeclaredConstructors()Returns all constructor methods of the runtime class.
public Method[] getMethods()  Returns the public method of the runtime class.
public Method[] getDeclaredMethods()Returns all methods of the runtime class.
public Field[] getFields() Returns the public member variable of the runtime class.
public Field[] getDeclaredFields() Returns all member variables of the runtime class.

Method is used to describe the methods of the runtime class.

Common methods of Method:

methoddescribe
public Class<?> getReturnType() The return value type of the return method.
public Class<?>[] getParameterTypes() Returns a list of parameters for the method.
public int getModifiers() Returns the access modifier of the method.
public String getName(); Returns the method name.

Field is used to describe the member variables of the runtime class.

Common methods of Field:

methoddescribe
public int getModifiers()  Returns the access modifier for a member variable.
public Class<?> getType()  Returns the data type of the member variable.
public String getName()  Returns the name of the member variable.

Application of reflection

The application of reflection in practice is mainly to dynamically create objects and dynamically call object methods.

1. Create object:

Call the newInstance() method of Class class to create the object.

2. Call the specified method:

(1) Get a Method object through the getMethod(String name,Class... parameterTypes) Method of Class class, and set the parameter type required for this Method operation.

(2) Call the Object invoke(Object obj, Object[] args) method and pass the parameter information of the target obj object to the method.

First look at a simple example to understand how Java's reflection mechanism works.

package com.chenHao.reflection;

import java.lang.reflect.Method;

/**
 * Java Reflection practice.
 * 
 * @author chenHao
 */
public class ForNameTest {

    /**
     * Entry function.
     * 
     * @param args
     *            parameter
     * @throws Exception
     *             error message
     */
    public static void main(String[] args) throws Exception {
        // Get Class
        Class<?> cls = Class.forName("java.lang.String");
        // Method to obtain the corresponding object through Class
        Method[] methods = cls.getMethods();
        // Output each method name
        for (Method method : methods) {
            System.out.println(method);
        }
    }
}

The following results are output:

public boolean java.lang.String.equals(java.lang.Object)
public java.lang.String java.lang.String.toString()
public int java.lang.String.hashCode()
public int java.lang.String.compareTo(java.lang.String)
public int java.lang.String.compareTo(java.lang.Object)
public int java.lang.String.indexOf(int)
public int java.lang.String.indexOf(int,int)
public int java.lang.String.indexOf(java.lang.String)
public int java.lang.String.indexOf(java.lang.String,int)
public static java.lang.String java.lang.String.valueOf(int)
public static java.lang.String java.lang.String.valueOf(char)
public static java.lang.String java.lang.String.valueOf(boolean)
public static java.lang.String java.lang.String.valueOf(float)
public static java.lang.String java.lang.String.valueOf(char[],int,int)
public static java.lang.String java.lang.String.valueOf(double)
public static java.lang.String java.lang.String.valueOf(char[])
public static java.lang.String java.lang.String.valueOf(java.lang.Object)
public static java.lang.String java.lang.String.valueOf(long)
public char java.lang.String.charAt(int)
public int java.lang.String.codePointAt(int)
public int java.lang.String.codePointBefore(int)
public int java.lang.String.codePointCount(int,int)
public int java.lang.String.compareToIgnoreCase(java.lang.String)
public java.lang.String java.lang.String.concat(java.lang.String)
public boolean java.lang.String.contains(java.lang.CharSequence)
public boolean java.lang.String.contentEquals(java.lang.CharSequence)
public boolean java.lang.String.contentEquals(java.lang.StringBuffer)
public static java.lang.String java.lang.String.copyValueOf(char[])
public static java.lang.String java.lang.String.copyValueOf(char[],int,int)
public boolean java.lang.String.endsWith(java.lang.String)
public boolean java.lang.String.equalsIgnoreCase(java.lang.String)
public static java.lang.String java.lang.String.format(java.lang.String,java.lang.Object[])
public static java.lang.String java.lang.String.format(java.util.Locale,java.lang.String,java.lang.Object[])
public byte[] java.lang.String.getBytes(java.lang.String) throws java.io.UnsupportedEncodingException
public void java.lang.String.getBytes(int,int,byte[],int)
public byte[] java.lang.String.getBytes()
public byte[] java.lang.String.getBytes(java.nio.charset.Charset)
public void java.lang.String.getChars(int,int,char[],int)
public native java.lang.String java.lang.String.intern()
public boolean java.lang.String.isEmpty()
public int java.lang.String.lastIndexOf(java.lang.String)
public int java.lang.String.lastIndexOf(int,int)
public int java.lang.String.lastIndexOf(int)
public int java.lang.String.lastIndexOf(java.lang.String,int)
public int java.lang.String.length()
public boolean java.lang.String.matches(java.lang.String)
public int java.lang.String.offsetByCodePoints(int,int)
public boolean java.lang.String.regionMatches(boolean,int,java.lang.String,int,int)
public boolean java.lang.String.regionMatches(int,java.lang.String,int,int)
public java.lang.String java.lang.String.replace(java.lang.CharSequence,java.lang.CharSequence)
public java.lang.String java.lang.String.replace(char,char)
public java.lang.String java.lang.String.replaceAll(java.lang.String,java.lang.String)
public java.lang.String java.lang.String.replaceFirst(java.lang.String,java.lang.String)
public java.lang.String[] java.lang.String.split(java.lang.String)
public java.lang.String[] java.lang.String.split(java.lang.String,int)
public boolean java.lang.String.startsWith(java.lang.String)
public boolean java.lang.String.startsWith(java.lang.String,int)
public java.lang.CharSequence java.lang.String.subSequence(int,int)
public java.lang.String java.lang.String.substring(int)
public java.lang.String java.lang.String.substring(int,int)
public char[] java.lang.String.toCharArray()
public java.lang.String java.lang.String.toLowerCase()
public java.lang.String java.lang.String.toLowerCase(java.util.Locale)
public java.lang.String java.lang.String.toUpperCase()
public java.lang.String java.lang.String.toUpperCase(java.util.Locale)
public java.lang.String java.lang.String.trim()
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()

This lists all the method names, their qualifiers, return types and exceptions thrown of the java.lang.String class. This program loads the specified class using the Class class forName method, and then calls the getMethods method to return the list of methods for the specified class. java.lang.reflect.Method is used to express a single method in a class.

To use java's reflection mechanism, you generally need to follow three steps:

  1. Get the Class object of the Class you want to operate on

  2. Obtain the method or property name of the operation Class through the Class object obtained in the first step

  3. The method or property obtained in the second step of the operation

When Java is running, no matter how many objects a Class generates, they will correspond to the same Class object, which represents the classes and interfaces in the running program. There are three common ways to obtain the Class object of an operation Class:

  1. Call the static method forName of Class, as in the above example;

  2. Use the. Class syntax of the class, such as class   cls = String.class;

  3. Call the getClass method of the object, such as String str = "abc"; Class   cls = str .getClass();

The following will describe how to execute a method of an object through the three steps described above through an example:

package com.chenHao.reflection;

import java.lang.reflect.Method;

/**
 * Java Reflection practice.
 *
 * @author chenHao
 */
public class ReflectionTest {
    public static void main(String[] args) throws Exception {
        DisPlay disPlay = new DisPlay();
        // Get Class
        Class<?> cls = disPlay.getClass();
        // Get the show method of the play Class through Class
        Method method = cls.getMethod("show", String.class);
        // Call the show method
        method.invoke(disPlay, "chenHao");
    }
}

class DisPlay {
    public void show(String name) {
        System.out.println("Hello :" + name);
    }
}

As mentioned earlier, each Class of a Java program will have a Class object corresponding to it. The first step in Java reflection is to get the Class object, as shown in line 14 of the code. Of course, the Method of each Class must also have a Method object corresponding to it. To call this Method through reflection, first obtain the Method object of this Method, such as line 16 of code, and then call this Method in turn with the Method object, such as line 18 of code.

Note: the first parameter of the 16 line getMethod method is the method name and the second is the parameter type of the method. If there are multiple parameters, then add the parameters, because getMethod is a variable parameter method. Execute the invoke method of 18 lines of code, which is actually executing the show method. Note that the first parameter of invoke is an object of the play class, that is, the show method of which object of the play class is called. The second parameter is the parameter passed to the show method. The type and number must be consistent with the getMethod method of line 16.

The above example describes how to call the method of a class through reflection. The following example describes how to create an object of a class at runtime and how to assign values to the properties of a class through reflection:

package com.chenHao.reflection;

import java.lang.reflect.Field;

/**
 * Java Reflection attribute exercise.
 *
 * @author chenHao
 */
public class ReflectionTest {
    public static void main(String[] args) throws Exception {
        // Establish student object
        Student student = new Student();
        // Assign values to student objects
        student.setStuName("chenHao");
        student.setStuAge(24);
        // Create copy target object
        Student destStudent = (Student) copyBean(student);
        // Output copy results
        System.out.println(destStudent.getStuName() + ":"
                + destStudent.getStuAge());
    }

    /**
     * Copy Student object information.
     *
     * @param from
     *            Copy source object
     * @param dest
     *            Copy target object
     * @throws Exception
     *             exception
    */
    private static Object copyBean(Object from) throws Exception {
        // Gets the Class object of the copy source object
        Class<?> fromClass = from.getClass();
        // Gets the attribute list of the copy source object
        Field[] fromFields = fromClass.getDeclaredFields();
        // Gets the Class object of the copy target object
        Object ints = fromClass.newInstance();
        for (Field fromField : fromFields) {
            // Set accessibility of properties
            fromField.setAccessible(true);
            // Assign the value of the attribute of the copy source object to the corresponding attribute of the copy target object
            fromField.set(ints, fromField.get(from));
        }

        return ints;
    }
}

/**
 * Students.
 */
class Student {
    /** full name */
    private String stuName;
    /** Age */
    private int stuAge;

    public String getStuName() {
        return stuName;
    }

    public void setStuName(String stuName) {
        this.stuName = stuName;
    }

    public int getStuAge() {
        return stuAge;
    }

    public void setStuAge(int stuAge) {
        this.stuAge = stuAge;
    }
}

Note: Field provides get and set methods to obtain and set the value of the property. However, since the property is a private type, it is necessary to set the accessibility of the property to true, such as lines 50 ~ 51 of the code. You can also set accessibility for the entire fields. Under line 40, use the static method setAccessible of AccessibleObject, such as AccessibleObject. setAccessible (from fields, true);

Simplified version of MyBatis tool

Requirements:

Create a tool class to query the database, automatically encapsulate the result set queried by SQL statement into different objects and return, a simplified version of MyBatis tool.

Idea:

Parameter list of tool class query method: Connection object, SQL statement, target runtime class object clazz, id value of data table.

1. Query the corresponding result set through Connection object, SQL statement and id value.

2. Use the reflection mechanism to call clazz's parameterless construction method to create the target object.

3. Get the file of clazz, that is, all member variables of the target class.

4. Find the result set field with the same name as the member variable and get the field value.

5. Find the corresponding setter method through the member variable name.

6. Use reflection mechanism to call setter method to complete assignment.

Implementation steps:

1. Import mysql driver and c3p0 data source related jar package.

2. Create c3p0-config.xml.

 

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>

    <named-config name="testc3p0">

        <!-- Specifies the basic properties of the connected data source -->
        <property name="user">root</property>
        <property name="password">root</property>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/school?useUnicode=true&amp;characterEncoding=UTF-8</property>

        <!-- If the number of connections in the database is insufficient, How many connections are requested from the database server at a time -->
        <property name="acquireIncrement">5</property>
        <!-- The number of connections when initializing the database connection pool -->
        <property name="initialPoolSize">5</property>
        <!-- The minimum number of database connections in the database connection pool -->
        <property name="minPoolSize">5</property>
        <!-- The maximum number of database connections in the database connection pool -->
        <property name="maxPoolSize">10</property>

        <!-- C3P0 The database connection pool can be maintained Statement Number of -->
        <property name="maxStatements">20</property>
        <!-- Each connection can be used at the same time Statement Number of objects -->
        <property name="maxStatementsPerConnection">5</property>

    </named-config>

</c3p0-config>

3. Create the data table student, user.

DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(11) DEFAULT NULL,
  `address` varchar(11) DEFAULT NULL,
  `tel` varchar(255) DEFAULT NULL,
  `score` double(11,1) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

4. Create entity classes Student and User.  

package com.southwind.entity;

public class Student {
    private int id;
    private String name;
    private String address;
    private String tel;
    private double score;
    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 String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    public String getTel() {
        return tel;
    }
    public void setTel(String tel) {
        this.tel = tel;
    }
    public double getScore() {
        return score;
    }
    public void setScore(double score) {
        this.score = score;
    }
    @Override
    public String toString() {
        return "Student [id=" + id + ", name=" + name + ", address=" + address
                + ", tel=" + tel + ", score=" + score + "]";
    }

}
package com.southwind.entity;

public class User {
    private int id;
    private String name;
    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;
    }
    @Override
    public String toString() {
        return "User [id=" + id + ", name=" + name + ", age=" + age + "]";
    }

}

5. Create the JDBC tools tool class.

package com.southwind.util;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class JDBCTools {
    private static ComboPooledDataSource dataSource;

    static{
        dataSource = new ComboPooledDataSource("testc3p0");
    }

    /**
     * Get Connection
     * @return
     */
    public static Connection getConnection(){
        try {
            return dataSource.getConnection();
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }

    /**
     * Release resources
     * @param conn
     * @param stmt
     * @param rs
     */
    public static void release(Connection conn,Statement stmt,ResultSet rs){
        if(conn != null){
            try {
                conn.close();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        if(stmt != null){
            try {
                stmt.close();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        if(rs != null){
            try {
                rs.close();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

6. Create the database query tool class MyQueryRunner, the core code.

package com.southwind.util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;

/**
 * General tools
 * @author southwind
 *
 */
public class MyQueryRunner {

    /**
     * Dynamically encapsulate result sets into objects
     * @param conn
     * @param sql
     * @param clazz
     * @param id
     * @return
     */
    public Object query(Connection conn,String sql,Class clazz,int id){
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        Object obj = null;
        try {
            pstmt = conn.prepareStatement(sql);
            pstmt.setInt(1, id);
            rs = pstmt.executeQuery();
            obj = clazz.newInstance();
            if(rs.next()){
                //Traverse the entity class attribute collection and assign the values in the result set to the attributes in turn
                Field[] fields = clazz.getDeclaredFields();
                //Get ResultSet data
                ResultSetMetaData rsmd = rs.getMetaData();
                for(int i = 0; i < fields.length; i++){
                    Object value = setFieldValueByResultSet(fields[i],rsmd,rs);
                    //Find the corresponding setter method through the property name
                    String name = fields[i].getName();
                    name = name.substring(0, 1).toUpperCase() + name.substring(1);
                    String MethodName = "set"+name;
                    Method methodObj = clazz.getMethod(MethodName,fields[i].getType());
                    //Call the setter method to complete the assignment
                    methodObj.invoke(obj, value);
                }
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return obj;
    }

    /**
     * Assign the value in the result set to the corresponding attribute according to the
     * @param field
     * @param rsmd
     * @param rs
     * @return
     */
    public Object setFieldValueByResultSet(Field field,ResultSetMetaData rsmd,ResultSet rs){
        Object result = null;
        try {
            int count = rsmd.getColumnCount();
            for(int i=1;i<=count;i++){
                //A result set field with the same property name was found
                if(field.getName().equals(rsmd.getColumnName(i))){
                    //Gets the data type of the property
                    String type = field.getType().getName();
                    switch (type) {
                        case "int":
                            result = rs.getInt(field.getName());
                            break;
                        case "java.lang.String":
                            result = rs.getString(field.getName());
                            break;
                        case "double":
                            result = rs.getDouble(field.getName());
                            break;
                    }
                }
            }
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return result;
    }
}

7. Test

Query the student table by id, call the tool method, and directly return the student object.

package com.southwind.test;

import java.sql.Connection;
import com.southwind.entity.User;
import com.southwind.util.MyQueryRunner;
import com.southwind.util.JDBCTools;

public class Test {
    public static void main(String[] args) {
        Connection conn = JDBCTools.getConnection();
        String sql = "select * from users where id = ?";
        MyQueryRunner myQueryRunner = new MyQueryRunner();
        User user = (User) myQueryRunner.query(conn, sql, User.class, 30);
        System.out.println(user);
    }
}

Supplement: when obtaining the methods, properties and constructors of the class, there will be two corresponding methods: getXXX and getgetDeclaredXXX. The difference is that the former returns methods and properties with access permission of public, including those in the parent class; However, the latter returns the methods and properties of all access permissions, excluding those of the parent class.

Posted by sunwukung on Thu, 16 Sep 2021 19:43:34 -0700