1. Preface
The most powerful technology in Java: reflection! Why? Let's briefly recall the Spring framework again.
We know that Spring is the mainstream Java Web development framework and the most successful framework in the Java world. The framework is a lightweight open source framework with high cohesion and attraction. In the Spring framework, IoC (Inverse of Control) and AOP (Aspect Oriented Programming) are used as the kernel.
- IoC refers to giving Spring the right to create objects. Before using Spring, objects are created by using new. After using Spring, objects are created by the Spring framework. IoC is a programming idea, which is mainly used to reduce the coupling between codes. Specifically, when creating objects, you do not need to use the classic new to create objects, but use reflection and xml to separate factory objects from production objects, which improves flexibility.
- AOP is used to encapsulate the common behavior of multiple classes, encapsulate the logic that has nothing to do with business but is called by business modules, reduce the repeated code of the system and reduce the coupling between modules. In addition, AOP also solves some system level problems, such as logs, transactions, permissions, etc.
IoC is usually also called IoC container, or Spring container. It is mainly used to manage the instantiation and initialization of objects and the whole life cycle of objects from creation to destruction. Read the information in XML or Java annotations to get which objects need to be instantiated. When the IoC container instantiates an object, it uses the reflection technology in Java. Therefore, java reflection technology supports the whole underlying implementation.
Although I simply used some reflection techniques when learning Spring before, in fact, technology was soon forgotten. Therefore, in this article, we will introduce Java reflection technology in detail, and do some simple case study.
2. Reflection
Java reflection means that the program can get all the properties and methods of a class and get the instantiated object at run time. The code can be configured when running without linking in the source code. This reduces the coupling of the code. Basic reflection includes the following technologies:
- Get the object of a class according to a string;
- Get all public or private, static or instance fields, methods and properties of a class;
- Reflection on generic classes;
2.1 get the Class object representing the Class
2.1.1 getClass
That is, get the Class object representing this Class through the object of a Class. This is also the simplest, for example:
String name = "Zhang San"; Class<? extends String> aClass = name.getClass();
2.2.2 Class.forName
It is composed of the namespace of the class and the name of the class. For example:
try { Class<?> name = Class.forName("java.lang.String"); Constructor<?> constructor = name.getDeclaredConstructor(String.class); String o = (String) constructor.newInstance("123"); System.out.println(o); }catch (Exception e){ e.printStackTrace(); }
2.2.3 class attribute of class
Each class has a class attribute, so you can get class type objects, such as String.class used earlier.
2.2.4 TYPE attribute of basic TYPE
For wrapper classes of basic data types, they all have TYPE. Through this attribute, you can also get a Class instance representing this Class, such as:
public static void main(String[] args) throws ClassNotFoundException { Boolean flag = false; Class<? extends Boolean> name = flag.getClass(); Class<?> name1 = Class.forName("java.lang.Boolean"); Class<?> name2 = Boolean.class; // One more way Class<?> name3 = Boolean.TYPE; }
2.2 get class members
2.2.1 constructor
Java's access modifiers have no meaning for reflection. For example, a class is defined as a private constructor, which can also be obtained here. For example, a class Person is defined here to test:
static class Person{ private String name; private int age; private Person(){} public Person(String name, int age){ this.age = age; this.name = name; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } private String getName() { return name == null ? "Unknown name" : name; } public int getAge(int baseAge) { return age == 0 ? 0 : baseAge + age; } }
To test the parameterized and nonparametric constructions of a class, a simple test is performed here:
public static void main(String[] args) throws Exception { // ------------Private parameterless constructor---------------- Class<?> clazz = Person.class; Constructor<?> constructor = clazz.getDeclaredConstructor(); constructor.setAccessible(true); // Set accessibility Person person = (Person) constructor.newInstance(); System.out.println(person); // ------------Expose parameterized constructors---------------- constructor = clazz.getDeclaredConstructor(String.class, int.class); constructor.setAccessible(true); // Set accessibility person = (Person) constructor.newInstance("Zhan San", 123); System.out.println(person); }
According to the above case, we know that the Java access modifier does not have any meaning here. And there are two ways to get constructors, including with parameters and without parameters. If there are parameters, the class type that needs to be passed in is the parameter defined in the method. Of course, another method is provided in Java, that is:
// ------------Get all constructors without handling parameter types---------------- Constructor<?>[] constructors = clazz.getDeclaredConstructors(); for (int i = 0; i < constructors.length; i++) { Constructor<?> temp = constructors[i]; temp.setAccessible(true); Person person; if(i == 0){ person = (Person) temp.newInstance(); System.out.println(person); }else{ person = (Person) temp.newInstance("Li Si", 123); System.out.println(person); } }
2.2.2 general methods
Let's continue with the above case. For example, we need to execute getName and getAge methods to get the user's name / age:
public static void main(String[] args) throws Exception { // ------------Parameterized constructor---------------- Class<?> clazz = Person.class; Constructor<?> constructor = clazz.getDeclaredConstructor(String.class, int.class); constructor.setAccessible(true); // Set accessibility Object person = constructor.newInstance("Zhang San", 23); // =>Nonparametric general method Method getName = clazz.getDeclaredMethod("getName"); getName.setAccessible(true); String name = (String) getName.invoke(person, null); System.out.println(name); // =>General method with parameters Method getAge = clazz.getDeclaredMethod("getAge", int.class); getAge.setAccessible(true); int age = (int) getAge.invoke(person, 2); System.out.println(age); }
result:
2.2.3 static method
Similarly, a static method is added to the Person class for testing, such as:
public static void printUserInfo(String parameter){ System.out.println("Test static methods!, Incoming parameters:" + parameter); }
Then we test:
public static void main(String[] args) throws Exception { // ------------Static method---------------- Class<?> clazz = Person.class; Method printUserInfo = clazz.getDeclaredMethod("printUserInfo", String.class); printUserInfo.setAccessible(true); printUserInfo.invoke(null, "User"); }
Note that since the static method does not require an instance object of the class, a null object is passed in the invoke. Therefore, there is no need to obtain the constructor object here. Operation results:
2.2.4 private non static attributes
It should be noted that in the previous case. I define the Person class as a static inner class I test here, so you can directly access the private properties of the inner class in the outer class. In order to test private properties, it is obvious that class instance objects cannot be obtained through reflection, and then private properties can be directly located. Therefore, the previously defined Person is placed in a separate file, namely: Person.java. Then start the test:
public static void main(String[] args) throws Exception { // ------------Parametric construction method---------------- Class<?> clazz = Person.class; Constructor<?> constructor = clazz.getDeclaredConstructor(String.class, int.class); constructor.setAccessible(true); Object person = constructor.newInstance("Zhang San", 25); // -->Gets the name field of the private type Field name = clazz.getDeclaredField("name"); name.setAccessible(true); System.out.println("The user initialization name is:" + (String) name.get(person)); // -->Modify field name.set(person, "Li Si"); System.out.println("The modified user name is:" + (String) name.get(person)); }
Test results:
2.2.5 private static attributes
For operations similar to the previous operation of static methods, let's first define a static attribute field in the Person class, such as:
private static final String TAG = "Person";
According to similar processing operations, we only need to pass null in the first parameter:
public static void main(String[] args) throws Exception { // ------------Parametric construction method---------------- Class<?> clazz = Person.class; // -->Gets the name field of the private type Field tag = clazz.getDeclaredField("TAG"); tag.setAccessible(true); System.out.println("Static constant parameter TAG The value of is:" + (String) tag.get(null)); }
The result is:
Because I define it as a constant string here, I won't test the modification syntax here.
2.3 reflection on generic classes
As before, in order to test the case, first define a generic class:
/** * DCL-Lazy style * @param <T> */ public abstract class Singleton<T extends Person> { public Singleton(){} private volatile T mInstance; /** * The real work of creating objects is left to subclasses * @return */ protected abstract T create(); public T getInstance(){ if(mInstance == null){ synchronized (this){ if(mInstance == null){ mInstance = create(); } } } return mInstance; } }
The Singleton class above is a generic class. Note that it is also an abstract class. Therefore, when instantiating Singleton, you need to implement the abstract method create of this class. First, let's look at how normal calls are done:
public class Man{ // Normal use of abstract generic singletons private static final Singleton<Person> userBean = new Singleton<Person>() { @Override protected Person create() { return new Person("unknown", 23); } }; public static void main(String[] args) throws Exception { Person instance = userBean.getInstance(); System.out.println(instance); } }
Corresponding, written as:
public class Man{ // Normal use of abstract generic singletons private static final Singleton<Person> userBean = new Singleton<Person>() { @Override protected Person create() { return new Person("unknown", 23); } }; public static void main(String[] args) throws Exception { // reflex Class<?> clazz = Man.class; Constructor<?> constructor = clazz.getDeclaredConstructor(); constructor.setAccessible(true); Object obj = constructor.newInstance(); Field userBean = clazz.getDeclaredField("userBean"); userBean.setAccessible(true); Singleton<Person> o = (Singleton<Person>) userBean.get(obj); Person person = o.getInstance(); System.out.println(person); } }
Note that the Class object of the test Class is used here, because the abstract method needs to be implemented. So the userBean is used here.
3. Postscript
Of course, the above is just some primitive reflection usage. We will continue to learn about qoor in the future.
References