Reading this article requires a reflection basis.
Fixed several bugs at 09/12/16 and added some proxy content.
JDK dynamic proxy is based on interfaces. One or more interfaces must be implemented to be proxies. Only the methods in these interfaces are proxies. After reading the dynamic proxy API of JDK, it is easy to take a detour if there are no examples. So here is a simple example of the calculator.
// Adder.javapackage test;public interface Adder { int add(int a, int b);}
// AdderImpl.javapackage test;public class AdderImpl implements Adder { @Override public int add(int a, int b) { return a + b; }}
Now we have an interface adder and a class adderimpl that implements this interface, and write a test.
// Test.javapackage test;public class Test { public static void main(String[] args) throws Exception { Adder calc = new AdderImpl(); int result = calc.add(1, 2); System.out.println("The result is " + result); }}
Obviously, the console will output:
The result is 3
However, now we need to record some information after the splitter is used for testing, but the source code of adderimpl cannot be changed, just like this:
Proxy: invoke add() at 2009-12-16 17:18:06The result is 3
Dynamic proxy can easily solve this problem. We only need to write a custom calling processor (Implementation interface Java. lang. reflect. invokationhandler), and then use the class Java. lang. reflect. the static method in the proxy generates the adder proxy class and uses this proxy class as the original adder.
Step 1: Implement invokationhandler to define the actions that should be performed when a method is called.
To customize a class myhandler implementation interface java. Lang. Reflect. invokationhandler, there is only one method to be rewritten:
// Adderhandler. javapackage test; import Java. lang. reflect. invocationhandler; import Java. lang. reflect. method; Class adderhandler implements invocationhandler {/*** @ Param proxy next proxy will generate an instance of the proxy class for you. Note, it is not the method instance of the method called by the new adderimpl * @ Param method. If add () is called, it is the parameter passed in when the add () Method Instance * @ Param ARGs calls the method. If add () is called, The * @ return parameter of the add () parameter is used as the return value after the proxy is used. If add () is called, the return value after add () is called */@ override public object invoke (Object proxy, method, object [] ARGs) throws throwable {//...}}
After proxy is used, this method replaces the execution of all methods in all specified interfaces. In this example, when the adder. Add () method is called, invoke () is actually executed (). Therefore, in order to have the correct results, we need to manually call the add () method in the invoke () method. Let's take a look at the parameters of the invoke () method, which exactly meets all the conditions required by reflection, so we will immediately think of this:
Object returnValue = method.invoke(proxy, args);
If you did, congratulations, you fell into the trap carefully prepared by JDK for you. Proxy is the instance of the proxy class generated by JDK for you. In fact, it is the object pointed to by adder after the proxy is used. Because we have called adder. add (1, 2) to execute invoke (). If method is used in invoke. invoke (proxy, argS) will execute invoke. Yes, this is an endless loop. However, the invoke () method has no other parameters. The simplest solution is to add an attribute for myhandler to the object actually being proxy. Therefore, due to the cold humor of JDK, we need to add the following section to the Custom Handler:
// Proxy object private object target; Public adderhandler (object target) {this.tar get = target ;}
If you like it, you can add getter/setter. Next, invoke () can be used as follows:
// Adderhandler. javapackage test; import Java. lang. reflect. invocationhandler; import Java. lang. reflect. method; import Java. util. date; Class adderhandler implements invocationhandler {// Private object target to be proxy; Public adderhandler (object target) {this.tar get = target;} @ override public object invoke (Object proxy, method method, object [] ARGs) throws throwable {// call the method of the proxy object and obtain the returned object returnvalue = method. invoke (target, argS); // before and after calling a method, you can add some other logic system. out. println ("Proxy: invoke" + method. getname () + "() at" + new date (). tolocalestring (); // you can return any value you want to return. Return returnvalue ;}}
Step 2: Use Java. Lang. Reflect. Proxy provided by JDK to generate proxy objects.
You can use the newproxyinstance () method to generate a proxy object. Take out the signature of this method:
/*** @ Param loader class loader, used to load the generated proxy class. * @ Param interfaces the proxy interface. All methods of these interfaces are proxies. * @ Param h the handler class instance we created in step 1. * @ Return proxy object, which implements all interfaces to be proxy. */Public static object newproxyinstance (classloader loader, class <?> [] Interfaces, invocationhandler h) throws illegalargumentexception
This method will do this. It will implement all the interfaces you want to proxy with a class dynamically generated by the code, and rewrite all the methods in the interface to call invocationhandler. invoke () method. The code for this class is similar to this:
// Simulate the proxy generated by proxy. This class is dynamically generated and does not have the corresponding. Java file. Class adderproxy extends proxy implements adder {protected adderproxy (invocationhandler h) {super (h) ;}@ override public int add (int A, int B) {try {method M = adder. class. getmethod ("add", new class [] {Int. class, Int. class}); object [] ARGs = {a, B}; Return (integer) H. invoke (this, M, argS);} catch (throwable e) {Throw new runtimeexception (e );}}}
According to the API, all generated proxy classes are subclasses of proxy. Of course, you cannot see the generated code for this class, and the proxy also calls the API generation of the sun. xxx package. Generally, the byte code is generated directly. Then, use the classloader you provided to load the class and instantiate an object and return it as a proxy.
After reading this method, let's modify the main () method.
// Test. javapackage test; import Java. lang. reflect. invocationhandler; import Java. lang. reflect. proxy; public class test {public static void main (string [] ARGs) throws exception {adder calc = new adderimpl (); // classloader loader = test. class. getclassloader (); // interface class [] interfaces = {adder. class}; // method call processor, save the actual adderimpl reference invocationhandler H = new adderhandler (calc); // Add proxy C for Calc Alb = (adder) proxy. newproxyinstance (loader, interfaces, H);/* what? Do you have other requirements? * /// Another processor that saves the reference of the former processor // invocationhandler H2 = new xxoohandler (h); // Add a proxy // calc = (adder) proxy. newproxyinstance (loader, interfaces, H2); int result = Calc. add (1, 2); system. out. println ("the result is" + result );}}
What is the output result?
Proxy: invoke add() at 2009-12-16 18:21:33The result is 3
By comparing the previous results, you will find that I have been writing this item for more than an hour. Let's take a look at JDK lazy:
Target can be generated in the proxy class.
The actual method needs to be called manually. It can be seen that all methods in the proxy class have only one sentence: Return XXX. Invoke (ooo );
However, there is also a reason for this writing. Target manages its own method. You love to adjust the callback _ callback method. If it is called, I am afraid two methods are needed in the invocationhandler interface, it also determines the return and processing parameters.
Technorati labels: Java, JDK, dynamic proxy