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