Secular wanderer: a programmer who focuses on technical research

Say what I said before

As one of the oldest and most fundamental technologies in Java, I think we need to know more about it

reflection

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

Class loading process

loading

  • Pass the class filejavaLoad the binary data in the class into memory
  • createClassObject and points a reference to the opened binary memory space

linking

There are three steps:

  • Verify that the class file conforms to the standard

Sublime opens the class file and you can see the hexadecimal format, but I won’t write it here, so you can open it for yourself

Cafe Babe 0000 0034 + + + + + + + + + + + + + + + + + + + + + + + + + +Copy the code
  • Assign default values to member variables
public class T {
    int i = 8;
    public static void main(String[] args) {}}Copy the code

The I here is going to be assigned to the default value of int 0, which is int I = 0;

  • Symbolic references used in the constant pool are converted to usable memory content

initializing

Here is how the class is actually initialized, in this step:

  • Member variables assign initial values
  • <init>()Method is called, the wholeClassloaded

It doesn’t matter if you don’t understand it. It’s just a reference.

There are a lot of loading processes, such as’ class loaders’, ‘parent delegate mechanisms’, etc., which are discussed in more detail in the JVM

annotations

Tags that begin with @ describe classes, fields, methods, etc. are annotations, such as:

// This is it
@Override
public int hashCode(a) {
    return super.hashCode();
}
Copy the code

Here @override is an annotation, provided by the JDK. Annotations can be customized. Let’s use @Override to see what elements a custom annotation contains

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

First of all, it’s embellished by the at sign interface, and then there’s other stuff

  • @Target

@target indicates the scope of the annotation we defined, and the full scope can be seen with ElementType:

public enum ElementType {
    /** applies to classes, interfaces, enumerated classes */
    TYPE,
    /** represents fields, variables, including enumeration properties */
    FIELD,
    /** method */
    METHOD,
    /** 参数 */
    PARAMETER,
    /** constructor */
    CONSTRUCTOR,
    /** Local variable */
    LOCAL_VARIABLE,
    /** Annotation type */
    ANNOTATION_TYPE,
    
    /** does not apply to general classes, but to fixed files in package-info.java */
    PACKAGE,
    /** * Type parameter declaration **@since1.8 * /
    TYPE_PARAMETER,

    TYPE_USE
}
Copy the code

Multiple scopes can be defined

  • @Retention

Indicates how long annotations for annotated types will remain. If there are no reserved comments on the annotation type declaration, the RetentionPolicy defaults to retentionpolicy.class and contains the following sections:

public enum RetentionPolicy {
    /** * The annotation is discarded by the compiler */
    SOURCE,

    /** * Annotations remain, but will not be loaded by the JVM */
    CLASS,

    /** * is retained for the duration of the JVM, so it is possible to get annotated annotations */ by reflection
    RUNTIME
}
Copy the code

The above two are the most important, and the other two should also have a look:

  • @Documented

Generated Javadoc documentation will be used

  • @Inherited

A subclass may inherit this annotation from its parent class

Specific method of reflection

Based on the object

public class Person {
    private String kkals;

    public String getKkals(a) {
        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(a) {}public Emp(Long id, String name, Date birthd) {
        this.id = id;
        this.name = name;
        this.birthd = birthd;
    }

    public Long getId(a) {
        return id;
    }

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

    public String getName(a) {
        return name;
    }

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

    public Date getBirthd(a) {
        return birthd;
    }

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

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

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

Get the Class object

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

    printInfo(clazz);

    // Load the class object as a.class
    clazz = Emp.class;
    printInfo(clazz);

    // This is a little low, so the idea will be yellow, and then change to.class mode
    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("Java specification compliant names:" + clazz.getCanonicalName());
    System.out.println("Class modifier:" + clazz.getModifiers());
    System.out.println("= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =");
}
Copy the code

The above four ways, more are recommended to use the first and second way

For class modifiers, if we want to determine if the class or attribute is what we want, we can compare it like this:

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

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

Get member variables

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

    // The main method
    Field[] fields = clazz.getFields();
    for (Field field : fields) {
        printFiledInfo(field);
    }

    System.out.println("================== gorgeous delimiter =====================");
    
    // The main method
    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());    // The variable name azk
    System.out.println("Variable type:" + field.getType());    // The variable type is class java.lang.string
    System.out.println("Variable modifier:" + field.getModifiers());   // modifiers 1
    System.out.println("= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =");
}
Copy the code

GetFields () and getDeclaredFields() :

  • The former can only be retrieved from the Class objectpublicModifies a variable, and nothing else can be modified
  • The latter can get variables of all types in the Class object, but not the parent

Get data for variables

// Public can be obtained directly
Field fieldId = clazz.getDeclaredField("azk");
final Object o = clazz.newInstance();
System.out.println(fieldId.get(o));

// The private type can be accessed only if setAccessible is set to true, otherwise an error will be reported,
// After fetching, 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);
Copy the code

Get common methods

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

    // We can get all the objects in the Object, including the parent Object and the method in 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 retrieved
    methods = empClass.getDeclaredMethods();
    for(Method method : methods) { System.out.println(method.getName()); System.out.println(method.getReturnType()); System.out.println(method.getModifiers()); }}Copy the code

Operation method

// If there are arguments, then getMethod follows the type of the argument
Method setId = empClass.getMethod("setId", Long.class);
setId.invoke(emp, 1L);

// There are no arguments
Method method = empClass.getMethod("getId");
System.out.println(method.invoke(emp));
Copy the code

GetDeclaredMethod () is recommended if parent classes and Object methods are not required.

Get constructor

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()); }Copy the code

Generate objects from the constructor

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

Copy the code

To obtain annotations

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

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

// Get the specified annotation object
Annotation declaredAnnotation = clazz.getDeclaredAnnotation(Annotation.class);
Copy the code

Determines whether a specified annotation exists

if (clazz.isAnnotationPresent(Annotation.class)) {
    System.out.println("The annotation exists.");
}
Copy the code

Example: simple implementation of ORM framework

Define setting table name annotations

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

Define set column name annotations

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

Definition ignores column name annotations

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

Utility class: cache class

public class EntityCacheUtil {

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

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

        // The current tableName is not null
        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 humped class name to an underscore */
    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 fields * of this class@throws IOException
     * @throws ClassNotFoundException
     */
    public static void init(a) throws IOException, ClassNotFoundException {
        // Change the package name to its own
        finalList<Class<? >> fileList = PackageUtil.INSTANCE.getFileList("zopx.top.study.reflect.entity", Serializable.class, true);

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

    public staticMap<Class<? >, Field[]> getFieldMap() {returnFIELD_MAP; }}Copy the code

PackageUtil is a Class that scans all Class objects in a package and caches them. The code is too long, but the poM dependencies are shown below

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

Concrete implementation mode

Define the interface first, which is pretty much the way we use it

public interface BaseDao<T extends Serializable.K> {

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

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

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

    /** * get the list */
    List<T> getList(a);

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

Copy the code

The implementation class

public abstract class BaseDaoImpl<T extends Serializable.K> implements BaseDao<T.K> { Class<? > clazz;public BaseDaoImpl(a) {
        // Get the type of the generic object in the class
        Type type = this.getClass().getGenericSuperclass();
        if (type instanceofParameterizedType) { ParameterizedType p = (ParameterizedType) type; clazz = (Class<? >) p.getActualTypeArguments()[0]; }}@Override
    public int save(T entity) {
        // 1. Get the 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 Transate exists in the field, the setting will be skipped
            Transate transate = field.getDeclaredAnnotation(Transate.class);
            if (null! = transate)continue;

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

            String columnName = "";
            if (null == column) {
                // If no field name is set for this field, modify the hump
                columnName = EntityCacheUtil.toLine(field.getName());
            } else {
                columnName = column.columnName();
            }
            sql.append(columnName).append(",");

            // Get the value from 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 with the get method to get concrete 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(a) {
        return null;
    }

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

As you can see, we just printed out the SQL statement at the end, nothing else. We’ll talk about MySQL and JDBC, and we’ll come back to this example

The other methods are not written, but a method called save() can also string the above things together, so you can try to complete the other methods yourself

Actual invocation

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
Copy the code

The last word

Well, that’s the end of reflection. Let’s talk about MySQL and JDBC