Java Foundation Series: Reflection

Earthly Wanderer: a procedural ape focusing on technical research

What I said earlier

As an ancient and basic technology in Java, I think we still need to understand it

reflex

Reflection is the technology of analyzing class properties and methods through binary bytecode

Class loading process

loading

  • Loading the class file into memory through java actually loads the binary data in the class
  • Create a Class object and point the reference to the opened binary memory space

linking

There are three steps:

  • Check whether the class file meets the standard

Open the class file through sublime and you can see the hexadecimal format. There is not so much written here. You can open it yourself

// The first four bytes are in standard format, followed by the version number of jdk, followed by the number of constant pools, constant pools, etc
cafe babe 0000 0034
  • Assign default values to member variables
public class T {
    int i = 8;
    public static void main(String[] args) {
    }
}

In this step, I here will be assigned to the default value of int 0, that is, int i = 0;

  • The symbolic references used in the constant pool are converted into usable memory contents

initializing

Here is the process of class initialization. In this step:

  • Assign initial value to member variable
  • < init > () method is called and the whole Class is loaded

It doesn't matter if you don't understand. It's just a mention here.

In fact, there are many processes in the process of real loading, such as' classloader ',' parental delegation mechanism ', etc. we will talk about them in more detail in the jvm

annotation

The descriptions marked on classes, fields, methods and other places that start with @ are annotations, such as:

// That's it
@Override
public int hashCode() {
    return super.hashCode();
}

Here @ Override is the annotation provided by jdk. Annotations can be customized. We can use @ Override to see which elements are included in the customized annotations

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

interface, and then other things

  • @Target

@Target indicates the scope of the annotation defined by us. The full scope can be viewed through ElementType:

public enum ElementType {
    /** Acting on classes, interfaces and enumeration classes */
    TYPE,
    /** Represents fields, variables, including enumeration properties */
    FIELD,
    /** Methodologically */
    METHOD,
    /** parameter */
    PARAMETER,
    /** Construction method */
    CONSTRUCTOR,
    /** local variable */
    LOCAL_VARIABLE,
    /** Annotation type */
    ANNOTATION_TYPE,
    
    /** It does not work in general classes, but in the fixed file package info In Java */
    PACKAGE,
    /**
     * Type parameter declaration
     *
     * @since 1.8
     */
    TYPE_PARAMETER,

    TYPE_USE
}

Multiple scopes can be defined

  • @Retention

Indicates how long comments with comment types will remain. If there are no reserved comments on the annotation type declaration, the retention policy defaults to retentionpolicy Class, including the following parts:

public enum RetentionPolicy {
    /**
     * Annotations are discarded by the compiler
     */
    SOURCE,

    /**
	 * Annotations are retained, but are not loaded by the jvm
     */
    CLASS,

    /**
	 * It is kept during the jvm running, so the annotated annotation can also be obtained through reflection
     */
    RUNTIME
}

The above two are the most important. Let's take a look at the other two:

  • @Documented

Generating javadoc documents will use

  • @Inherited

The subclass can inherit the annotation in the parent class

Reflection specific method

Base object

public class Person {
    private String kkals;

    public String getKkals() {
        return kkals;
    }

    public void setKkals(String kkals) {
        this.kkals = kkals;
    }
}

public class Emp extends Person implements Comparable<Emp>{

    public String azk = "121";

    private Long id;
    private String name;
    private Date birthd;

    private Emp(Long id) {
        this.id = id;
    }

    public Emp() {
    }

    public Emp(Long id, String name, Date birthd) {
        this.id = id;
        this.name = name;
        this.birthd = birthd;
    }

    public Long getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public Date getBirthd() {
        return birthd;
    }

    public void setBirthd(Date birthd) {
        this.birthd = birthd;
    }

    @Override
    public java.lang.String toString() {
        return "Emp{" +
                "azk=" + azk +
                ", id=" + id +
                ", name=" + name +
                ", birthd=" + birthd +
                '}';
    }

    @Override
    public int compareTo(Emp o) {
        return 0;
    }
}

Get Class object

private static void getClazz() throws ClassNotFoundException {
    // Load the Class object by means of full package name + Class name
    Class<?> clazz = Class.forName("java.lang.String");

    printInfo(clazz);

    // Pass Class Load Class objects in Class mode
    clazz = Emp.class;
    printInfo(clazz);

    // In this way, it's a little low. If it's written like this, the idea will also report yellow, and then it will be modified into a class Class method
    clazz = new Emp().getClass();
    printInfo(clazz);

    // Not suitable for ordinary objects
    Class<Integer> type = Integer.TYPE;
    printInfo(type);
}

private static void printInfo(Class<?> clazz) {
    System.out.println("=======================================");
    System.out.println("Package name:" + clazz.getPackage());
    System.out.println("Class full name:" + clazz.getName());
    System.out.println("Class name:" + clazz.getSimpleName());
    System.out.println("accord with java Name of specification:" + clazz.getCanonicalName());
    System.out.println("Class modifier:" + clazz.getModifiers());
    System.out.println("=======================================");
}

Among the above four methods, the first and second methods are more recommended

For class modifiers, if we want to judge whether this class or attribute is what we want, we can compare it as follows:

(clazz.getModifiers() & Modifier.PRIVATE) != 0;

Returns true, indicating that it is modified by the specified modifier, otherwise it is not

Get member variable

private static void getField()  throws ClassNotFoundException{
    Class<?> clazz = Class.forName("Emp");

    // Main methods
    Field[] fields = clazz.getFields();
    for (Field field : fields) {
        printFiledInfo(field);
    }

    System.out.println("==================Gorgeous separator=====================");
    
    // Main methods
    Field[] declaredFields = clazz.getDeclaredFields();
    for (Field declaredField : declaredFields) {
        printFiledInfo(declaredField);
    }
}

private static void printFiledInfo(Field field) {
    System.out.println("=======================================");
    System.out.println("Variable name:" + field.getName());    // Variable name azk
    System.out.println("Variable type:" + field.getType());    // Variable type class java lang.String
    System.out.println("Variable modifier:" + field.getModifiers());   // Modifier 1
    System.out.println("=======================================");
}

Comparison between getFields() and getDeclaredFields():

  • The former can only get the variables modified by public in the Class object, and the others cannot be modified
  • The latter can get all types of variables in the Class object, but cannot get the variables of the parent Class

Get variable data

// If it is public, you can get it directly
Field fieldId = clazz.getDeclaredField("azk");
final Object o = clazz.newInstance();
System.out.println(fieldId.get(o));

// private type can only be obtained by setting setAccessible to true, otherwise an error will be reported,
// After obtaining, it is recommended to set it to false
Field fieldId = clazz.getDeclaredField("id");
final Object o = clazz.newInstance();
// important
fieldId.setAccessible(true);
System.out.println(fieldId.get(o));
// important
fieldId.setAccessible(false);

Get common methods

private static void getMethods() {
    Class<Emp> empClass = Emp.class;

    // You can get all the objects in the Object, including the parent Object and the methods in the Object
    Method[] methods = empClass.getMethods();
    for (Method method : methods) {
        System.out.println(method.getName());
        System.out.println(method.getReturnType());
        System.out.println(method.getModifiers());
    }

    System.out.println("======================================");

    // Only methods in the current class can be obtained
    methods = empClass.getDeclaredMethods();
    for (Method method : methods) {
        System.out.println(method.getName());
        System.out.println(method.getReturnType());
        System.out.println(method.getModifiers());
    }
}

Operation method

// If there are parameters, getMethod follows closely with the type of parameter
Method setId = empClass.getMethod("setId", Long.class);
setId.invoke(emp, 1L);

// There are no parameters here
Method method = empClass.getMethod("getId");
System.out.println(method.invoke(emp));

If the parent class and Object methods are not required, the getDeclaredMethod() method is more recommended,

Get construction method

Constructor<?>[] constructors = clazz.getConstructors();
for (Constructor<?> constructor : constructors) {
    System.out.println(constructor.getName());
}
System.out.println("========================");

constructors = clazz.getDeclaredConstructors();
for (Constructor<?> constructor : constructors) {
    System.out.println(constructor.getName());
}

Generate objects from constructor

Constructor<?> constructor = clazz.getDeclaredConstructor(Long.class, String.class, Date.class);
        System.out.println(((Emp)constructor.newInstance(1L, "kkals", new Date())).toString());

Get annotation

// Get all the annotations on the class
Annotation[] annotations = clazz.getDeclaredAnnotations();

for (Annotation annotation : annotations) {
    System.out.println(annotation);
}

// Gets the specified annotation object
Annotation declaredAnnotation = clazz.getDeclaredAnnotation(Annotation.class);

Determine whether the specified annotation exists

if (clazz.isAnnotationPresent(Annotation.class)) {
    System.out.println("The annotation does not exist");
}

Example: simple implementation of ORM framework

Define and set table name annotation

@Target(value = ElementType.TYPE)
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Table {
    public String tableName() default "";
}

Define and set column name annotations

@Target(value = {ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
    String columnName() default "";
}

Define ignore column name annotation

@Target(value = ElementType.FIELD)
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Transate {
}

Tool class: cache class

public class EntityCacheUtil {

    private static final Map<Class<?>, Field[]> FIELD_MAP = new HashMap<>();
    private static final Map<Class<?>, String> TABLE_NAME_MAP = new HashMap<>();

    static {
        try {
            EntityCacheUtil.init();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * Get table name
     */
    public static String getTableName(Class<?> clazz) {
        String tableName = TABLE_NAME_MAP.get(clazz);

        // This is to judge whether the current tableName is not empty
        if (StringUtils.isNotBlank(tableName)) {
            return tableName;
        }
        Table annotation = clazz.getDeclaredAnnotation(Table.class);
        if (null != annotation) {
            tableName = annotation.tableName();
        } else {
            tableName = toLine(clazz.getSimpleName());
        }
        TABLE_NAME_MAP.put(clazz, tableName);
        return tableName;
    }

    /**
     * Convert the class name of hump identification into underline
     */
    public static String toLine(String simpleName) {
        final char[] chars = simpleName.toCharArray();
        StringBuilder sb = new StringBuilder();
        for (char c : chars) {
            if (Character.isUpperCase(c)) {
                sb.append("_");
            }
            sb.append(c);
        }
        if (sb.toString().startsWith("_")) {
            sb.delete(0, 1);
        }
        return sb.toString().toLowerCase();
    }

    /**
     * Get all the fields under this class
     * @throws IOException
     * @throws ClassNotFoundException
     */
    public static void init() throws IOException, ClassNotFoundException {
        // Change the package name to your own
        final List<Class<?>> fileList = PackageUtil.INSTANCE.getFileList("zopx.top.study.reflect.entity", Serializable.class, true);

        fileList.forEach(item -> FIELD_MAP.put(item, item.getDeclaredFields()));
    }

    public static Map<Class<?>, Field[]> getFieldMap() {
        return FIELD_MAP;
    }
}

For the PackageUtil Class, this Class mainly scans all Class objects under a package and caches them. The specific code will not be posted. It is too long. pom dependency is given below

<dependency>
    <groupId>top.zopx</groupId>
    <artifactId>tools-boot-starter</artifactId>
    <version>1.1.4</version>
</dependency>

Specific implementation mode

Define the interface first, which is basically our most commonly used method

public interface BaseDao<T extends Serializable, K> {

    /**
     * preservation
     */
    int save(T entity);

    /**
     * modify
     */
    int update(T entity, K id);

    /**
     * delete
     */
    int deleteById(K id);

    /**
     * Get list
     */
    List<T> getList();

    /**
     * Get details by ID
     */
    T getById(K id);
}

Implementation class

public abstract class BaseDaoImpl<T extends Serializable, K> implements BaseDao<T, K> {
    Class<?> clazz;

    public BaseDaoImpl() {
        // Gets the type of the generic object in the class
        Type type = this.getClass().getGenericSuperclass();
        if (type instanceof ParameterizedType) {
            ParameterizedType p = (ParameterizedType) type;
            clazz = (Class<?>) p.getActualTypeArguments()[0];
        }
    }

    @Override
    public int save(T entity) {
        // 1. Get table name and column
        String tableName = EntityCacheUtil.getTableName(entity.getClass());
        Field[] fields = EntityCacheUtil.getFieldMap().get(entity.getClass());

        // insert into user_acc(user_id, login_name, login_pwd) value(?,?,?);
        StringBuilder sql =
                new StringBuilder("insert into ")
                        .append(tableName)
                        .append("(");

        List<Object> valueList = new ArrayList<>(fields.length);

        for (Field field : fields) {
            // If there is a transition in the field, the setting is skipped
            Transate transate = field.getDeclaredAnnotation(Transate.class);
            if (null != transate)
                continue;

            Column column = field.getDeclaredAnnotation(Column.class);

            String columnName = "";
            if (null == column) {
                // It indicates that there is no field name set in this field, then modify the hump
                columnName = EntityCacheUtil.toLine(field.getName());
            } else {
                columnName = column.columnName();
            }
            sql.append(columnName).append(",");

            // Get value through field
            field.setAccessible(true);
            try {
                valueList.add(field.get(entity));
            } catch (IllegalAccessException e) {
                System.out.println(e.getMessage());
            }
            field.setAccessible(false);

            // The second way is to call invoke through the get method to get specific data
//            try {
//                Method method = entity.getClass().getMethod(getMethodName(field.getName()));
//                valueList.add(method.invoke(entity));
//            } catch (Exception e) {
//                System.out.println(e.getMessage());
//            }
        }
        sql.deleteCharAt(sql.length() - 1).append(") value (");
        valueList.forEach(value -> sql.append("?,"));
        sql.deleteCharAt(sql.length() - 1);
        sql.append(")");
		
        System.out.println(sql.toString());
        System.out.println(valueList);
        return 0;
    }

    private String getMethodName(String fieldName) {
        return "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
    }

    @Override
    public int update(T entity, K id) {
        return 0;
    }

    @Override
    public int deleteById(K id) {
        return 0;
    }

    @Override
    public List<T> getList() {
        return null;
    }

    @Override
    public T getById(K id) {
        System.out.println(clazz.getCanonicalName());
        return null;
    }
}

In fact, we can see that in the end, we just print out the sql statement without other operations (it doesn't matter if we don't understand the meaning of sql). Next, we will talk about MySQL and JDBC. Then we will come back and continue to improve this example

Other methods are not written. You can string the above things through a method of save(). You can try to complete other methods by yourself

Actual call mode

public interface UserAccDao extends BaseDao<UserAcc, Long> {
}

public class UserAccDaoImpl extends BaseDaoImpl<UserAcc, Long> implements UserAccDao {
}

public class Test {
    public static void main(String[] args) {
        UserAccDao userAccDao = new UserAccDaoImpl();

        UserAcc entity = new UserAcc();
        entity.setUserId(1L);
        entity.setLoginName("123");
        entity.setLoginPwd("12345");
        userAccDao.save(entity);

        userAccDao.getById(1L);
    }
}

// insert into user_acc(user_id,login_name,login_pwd) value (?,?,?)
// [1, 123, 12345]
// zopx.top.study.reflect.entity.UserAcc

Last words

Well, the reflection ends here. Let's talk about MySQL and JDBC

Tags: Java jvm Class reflection

Posted by Megalink on Thu, 31 Mar 2022 15:53:49 +0300