When we introduced the Java annotations in the previous article, we mentioned the Java reflection API several times. Unlike Javax.lang.model, the internal structure of the program at run time can be obtained through the reflection API. The dynamic proxy provided in the reflection API is also a very powerful feature that can natively implement the method interception functionality in AOP. Like the English word reflection, using the reflection API is like looking at a Java class's reflection in the water. Once you know the internal structure of a Java class, you can interact with it, including creating new objects and calling methods in the object. This interaction is the same as the effect that is used directly in the source code, but provides additional flexibility at run time. One of the biggest drawbacks of using reflection is poor performance. For the same operation, the time required to use the reflection API is approximately one or two orders of magnitude slower than the direct use. However, the performance of the reflection operation has been greatly improved in the current JVM implementation. There is always a tradeoff between flexibility and performance. Applications can use the reflection API at the right time.
Basic usage
The first major function of the Java reflection API is to get the internal structure of the program at run time. This is a very useful function for the checker and debugger of the program. With just more than 10 lines of code, you can iterate through the internal structure of a Java class, including its construction methods, declared fields, and defined methods. This has to be said to be a very powerful ability. As long as you have an object of the Java.lang.Class class, you can get to the constructor method, domain, and method in the class. The corresponding methods are GetConstructor, GetField and GetMethod respectively. These three methods also have the corresponding getdeclaredxxx version, the difference is that the getdeclaredxxx version of the method only gets the elements declared by the class itself, without regard to inheritance. The three classes of Constructor, field, and method represent the construction methods, fields, and methods in the class, respectively. The methods in these classes can get the metadata for the corresponding structure.
Another function of the reflection API is to manipulate a Java object at run time. These operations include dynamically creating an object of a Java class, getting the value of a domain, and invoking a method. The operations of classes and objects written in the Java source code can be implemented at run time through the reflection API. Consider the following simple Java class.
1 classMyClass {2 Public intcount;3 PublicMyClass (intstart) {4Count =start;5 }6 Public voidIncrease (intStep) {7Count = Count +Step;8 }9}
Using both the general approach and the reflection API is straightforward.
1MyClass MyClass =NewMyClass (0);//General Practice2Myclass.increase (2);3System.out.println ("Normal" +myclass.count);4 Try {5Constructor Constructor = MyClass.class. GetConstructor (int.class);//Get Construction Method6MyClass myclassreflect = constructor.newinstance (10);//Creating Objects7method = MyClass.class. GetMethod ("Increase",int.class);//Get Method8Method.invoke (Myclassreflect, 5);//Calling Methods9Field field = MyClass.class. GetField ("Count");//Get DomainTenSystem.out.println ("Reflect" + field.getint (myclassreflect));//get the value of a field One}Catch(Exception e) { A e.printstacktrace (); -}
Because of the specificity of the array, the array class provides a series of static methods used to create arrays and to access and manipulate elements in an array.
1 Object array = array.newinstance (String. Class// equivalent to new string[10]2 array.set (Array, 0, "Hello"); // equivalent to array[0] = "Hello" 3 Array.set (Array, 1, "World"); // equivalent to array[1] = "World" 4 System.out.println (array.get (Array, 0)); // equivalent to array[0]
The Java Reflection API can be used to bypass the Java default access control checks, such as the ability to directly get to the object's private domain value or call the private method. You only need to call the Setaccessible method and set to True after you have obtained the object for the constructor, field, and method classes. With this mechanism, it is convenient to get the internal state of the program at run time.
Handling generics
With the introduction of generics in Java 5, the Java Reflection API has been modified to provide support for generics. Because of the existence of the type erasure mechanism, information such as the type parameter in the generic class does not exist at run time. The JVM sees all the original types. In this respect, Java 5 has revised the format of the Java class file, adding the signature attribute to contain type information that is not in the JVM type system. For example, in the case of the Java.util.List interface, the declaration of the signature attribute in its class file is <E:Ljava/lang/Object;>Ljava/lang/Object; ljava/util/collection<te;>;; , which means that the list interface has a type parameter E. At run time, the JVM reads the contents of the Signature property and provides it to the reflection API for use.
For example, in code that declares a field to be of type list<string>, although its type becomes the original type list at runtime, it can still get the actual type parameter used by reflection.
1Field field = Pair.class. Getdeclaredfield ("MyList");//the type of mylist is List2Type type =Field.getgenerictype ();3 if(TypeinstanceofParameterizedtype) { 4Parameterizedtype Paramtype =(parameterizedtype) type; 5type[] Actualtypes =paramtype.getactualtypearguments (); 6 for(Type atype:actualtypes) {7 if(AtypeinstanceofClass) { 8Class CLZ =(Class) atype; 9System.out.println (Clz.getname ());//Output java.lang.StringTen } One } A}
Dynamic Agent
People who are familiar with design patterns may not be unfamiliar with the proxy model. The proxy object and the Proxied object generally implement the same interface, and the caller interacts with the proxy object. The presence of the agent is transparent to the caller, and the caller sees only the interface. The proxy object can encapsulate some internal processing logic, such as access control, remote communication, logging, caching, and so on. For example, an object access agent can add caching support on top of an ordinary access mechanism. This pattern has been widely used in RMI and EJB. The implementation of the traditional proxy mode requires adding some additional classes in the source code. These classes are usually handwritten or automatically generated by tools. The dynamic agent mechanism introduced in JDK 5 allows developers to dynamically create proxy classes and their objects at run time. At run time, you can dynamically create a proxy class that implements multiple interfaces. The object for each proxy class associates an implementation of the Invocationhandler interface that represents the internal processing logic. When a user invokes a method in the interface that the proxy object proxies, the message is passed to the Invocationhandler invoke method. In the parameters of the Invoke method, you can get to the proxy object, the method object corresponding to the methods, and the actual parameters of the call. The return value of the Invoke method is returned to the consumer. This practice is actually tantamount to intercepting method calls. People familiar with AOP should not be unfamiliar with this mode of use. However, this approach does not need to rely on an AOP framework such as ASPECTJ.
The following code is used to proxy an object that implements the list interface. The functionality implemented is also very simple, which is to prohibit the use of the Add method in the list interface. If an object that implements the list interface is passed in the GetList, the actual return is a proxy object, and attempting to invoke the Add method on the object throws an exception.
1 PublicList GetList (FinalList List) {2 return(List) Proxy.newproxyinstance (Dummyproxy.class. getClassLoader (),NewClass[] {List.class },3 NewInvocationhandler () {4 PublicObject Invoke (Object proxy, Method method, object[] args)throwsThrowable {5 if("Add". Equals (Method.getname ())) {6 Throw Newunsupportedoperationexception ();7 }8 Else {9 returnMethod.invoke (list, args);Ten } One } A }); -}
The actual flow here is that the Invoke method in Invocationhandler is called when the proxy object's Add method is called. The parameter method contains the basic information of the call. Because the method name is add, the associated exception is thrown. If another method is called, the original logic is executed.
Use case
The presence of the Java Reflection API adds some degree of dynamism to the Java language, enabling the ability to function in some dynamic languages. In JavaScript code, for example, the obj["set" + propname] () can be used to find the corresponding method to call according to the value of the variable propname. Although it is not possible to write this in Java source code, similar functionality can be achieved through the reflection API. This is useful for dealing with some legacy code. For example, there are multiple versions of the classes that need to be used, and each version provides different method names and parameters. And the calling code has to work in tandem with these different versions, so you can check the actual class for a selective invocation by using the reflection API.
The Java Reflection API actually defines a contract that is more loosely relative to the time of compilation. If the called Java object does not contain a method and is referenced in the caller code, an error occurs at compile time. The reflection API can then defer such checks to run time. By combining byte code enhancement, ClassLoader, and reflection APIs in Java, you can handle scenarios with high flexibility requirements.
In some cases, it may be necessary to load a Java class from the far end to execute. For example, a client Java program can download Java classes from the server side of the network to implement the automatic update mechanism. When the code logic needs to be updated, only a new Java class needs to be deployed to the server side. As a general practice, after the class-byte code is downloaded by the custom ClassLoader, the object of class is defined, and the instance can be created by the Newinstance method. However, this approach requires that both the client and the server have a definition of an interface, and the implementation of this interface is downloaded from the server side. This allows the required type conversions to be performed on the client, and the object instance is used through the interface. Use the Reflection API if you want a more loosely contracted client and server side. The contract between the two requires only the name of the method and the level of the parameter is sufficient. The server-side Java class does not need to implement a specific interface, it can be a generic Java class.
The usage scenarios for dynamic proxies are even more extensive. Dynamic proxies are available wherever you need to use the method interception feature in AOP. The AOP implementation of the spring framework also uses dynamic proxies by default. However, dynamic proxies in the JDK only support proxies to interfaces, and cannot provide proxies for a common Java class. But this implementation is sufficient for most of the time.
References
- Classworking toolkit:reflecting Generics
- D?ecorating with dynamic proxies
Java Reflection Dynamic Proxy