Java dynamic proxy implementation of the proxy pattern

  • 2020-04-01 01:10:59
  • OfStack

Today, by chance, I suddenly wanted to look at the dynamic proxy of JDK, because I knew a little about it before and just wanted to test it out. I wrote several interfaces and classes in a short time:
Interface class: userservice.java


package com.yixi.proxy;
public interface UserService {
    public int save() ;
    public void update(int id);
}

Implementation class: userserviceimpl.java

package com.yixi.proxy;
public class UserServiceImpl implements UserService {
    @Override
    public int save() {
        System.out.println("user save....");
        return 1;
    }
    @Override
    public void update(int id) {
        System.out.println("update a user " + id);
    }
}

Then I wrote my InvocationHandler in a hurry: it's very simple to record the start and end times of method execution
TimeInvocationHandler. Java

package com.yixi.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class TimeInvocationHandler implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("startTime : " +System.currentTimeMillis());
        Object obj = method.invoke(proxy, args);
        System.out.println("endTime : " +System.currentTimeMillis());
        return obj;
    }
}

All the prep work is done and of course we're going to start writing tests!
Test. The Java

package com.yixi.proxy;
import java.lang.reflect.Proxy;
public class Test {
    public static void main(String[] args) { 9         TimeInvocationHandler timeHandler = new TimeInvocationHandler();
        UserService u =  (UserService) Proxy.newProxyInstance(UserServiceImpl.class.getClassLoader(), UserServiceImpl.class.getInterfaces(), timeHandler);
        u.update(2);
        u.save();
    }
}

It runs happily, but it doesn't give you much credit. The result is a screen full of exceptions:

startTime : 1352877835040
startTime : 1352877835040
startTime : 1352877835040
Exception in thread "main" java.lang.reflect.UndeclaredThrowableException
    at $Proxy0.update(Unknown Source)
    at com.yixi.proxy.Test.main(Test.java:11)
Caused by: java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at com.yixi.proxy.TimeInvocationHandler.invoke(TimeInvocationHandler.java:12)
    ... 2 more

Com. Yixi. Proxy. TimeInvocationHandler. Invoke (TimeInvocationHandler. Java: 12) anomalies clearly told the problem is in TimeInvocationHandle 12 rows: namely

public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("startTime : " +System.currentTimeMillis());
        Object obj = method.invoke(proxy, args);
        System.out.println("endTime : " +System.currentTimeMillis());
        return obj;
    }

There is nothing wrong with the method. Because the method called invoke() seems to provide all the parameters required by method.invoke(Object,Object[]), we will naturally use it. If you really think that way, you will fall into the trap of JDK. Let's see the correct way to write it first, in case some students are not in the mood to read the following method at least give a correct solution:
Modify TimeInvocationHandler. Java

package com.yixi.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class TimeInvocationHandler implements InvocationHandler {
    private Object o;
    public TimeInvocationHandler(Object o){
        this.o = o;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("startTime : " +System.currentTimeMillis());
        Object obj = method.invoke(o, args);
        System.out.println("endTime : " +System.currentTimeMillis());
        return obj;
    }
}

Modify the Test. The Java

package com.yixi.proxy;
import java.lang.reflect.Proxy;
public class Test {
    public static void main(String[] args) {
        TimeInvocationHandler timeHandler = new TimeInvocationHandler(new UserServiceImpl());
        UserService u =  (UserService) Proxy.newProxyInstance(UserServiceImpl.class.getClassLoader(), UserServiceImpl.class.getInterfaces(), timeHandler);
        u.update(2);
        u.save();
    }
}

Now the correct output:

startTime : 1352879531334
update a user 2
endTime : 1352879531334
startTime : 1352879531334
user save....
endTime : 1352879531335

If you want less code, you can directly write anonymous classes:
Package com. Yixi. Proxy;
Import the Java. Lang. Reflect. InvocationHandler;
Import the Java. Lang. Reflect. Method;
Import the Java. Lang. Reflect. Proxy;
Public class Test {
      Public static void main(String[] args) {
              Final UserServiceImpl usi = new UserServiceImpl();
              UserService u =   (UserService) Proxy newProxyInstance (
                              Usi. GetClass (). GetClassLoader (),
                              Usi. GetClass (). GetInterfaces (),
                              New InvocationHandler () {
                                      @ Override
                                      Public Object invoke(Object proxy, Method Method, Object[] args)
                                                      Throws Throwable {
                                              System. The out. Println (" startTime: "+ System. CurrentTimeMillis ());
                                              Object obj = method.invoke(usi, args);
                                              System. The out. Println (" endTime: "+ System. CurrentTimeMillis ());
                                              Return obj.
                                      }
                              });
              U.u pdate (2);
              U.s ave ();
      }
}
Since the method. The invoke (target, args); So the first parameter in this is passed in is the target Object so why does the Invoke method of invocationHandler have an Object proxy parameter? Read on!
For the most important method, invoke (personally), let's take a look at what the JDK says:

invoke
Object invoke(Object proxy,
              Method method,
              Object[] args)
              throws Throwable Process the method invocation on the proxy instance and return the result. When a method is invoked on the proxy instance associated with the method, it is invoked on the invocation handler.  
 Parameters: 
proxy -  The proxy instance on which the method is invoked 
method -  Corresponds to the interface method invoked on the proxy instance  Method  Instance. Method  The declarative class of the object will be the interface in which the method is declared, which can be the superinterface of the proxy interface on which the proxy class inherits the method. 
args -  An array of objects containing the parameter values passed into the method call on the proxy instance, or if the interface method does not use the parameter  null . The parameters of the base type are wrapped in the appropriate base wrapper class (e.g  java.lang.Integer  or  java.lang.Boolean ). 

Proxy - the proxy instance on which the method is invoked? What does this sentence mean? The agent? Method is an agent method, right? Then the method I execute the proxy should be Object obj = method.invoke(proxy, args); ? At that time I also did not turn around, to discuss the group, to Google did not find any inspiration, think or look at the source code it may be able to see something!
Open the source code of the Proxy class to find out how to construct a method:

protected InvocationHandler h;
protected Proxy(InvocationHandler h) {
    this.h = h;
    }

Take the InvocationHandler as an argument to the Proxy's constructor... So what does it do with InvocationHandler? Is there any connection to the invoke() method in InvocationHandler?
The first thing that occurred to me was that inside the Proxy, the following statement would be called:

h.invoke(this,methodName,args);

Because you have to call the invoke method to execute the method,
So let's look at this first

< img Alt = "" border = 0 SRC =" / / files.jb51.net/file_images/article/201211/201211150853313.jpg ">

Here you can see that it looks a little bit like this: when u.pdate (2), aProxy calls handler. Invoke (proxyClass,update,2) a, which calls proxyclass.update (2);
When the u.s ave (); When aProxy will call handler.invoke(proxyClass,save,null) a, that is, call proxyclass.save ();

When test.java changes to this:


public class Test {
    public static void main(String[] args) {
        final UserServiceImpl usi = new UserServiceImpl();
        UserService u =  (UserService) Proxy.newProxyInstance(
                usi.getClass().getClassLoader(),
                usi.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args)
                            throws Throwable {
                        return null;
                    }
                });
        u.update(2);
        u.save();
    }
}

Notice that the method of the anonymous class returns null.

Exception in thread "main" java.lang.NullPointerException
    at $Proxy0.save(Unknown Source)
    at com.yixi.proxy.Test.main(Test.java:17)

Student: 17 lines of null Pointers which is the u. ave() method here is the element that has a null is u empty? No. If u is null, u.u.pdate (2) will declare a null pointer exception there. When I comment out line 17, the exception is gone, which means that u.u.pdate () can execute normally. So why is that?
This is why the invoke method returns null:
Notice the two methods in the UserService class:

public interface UserService {
    public int save() ;
    public void update(int id);
}

The Save() method returns an int and the update method returns a void; The above guess is that handler.invoke() implements proxyclass.update (2); The return method in the invoke method is the return value of the corresponding proxy method,
So when the invoke method returns null, the proxy update method receives the return value as null, and it returns void so there is no exception, and the proxy save has to return a value of type int. We still return null here, and the JVM cannot convert null to int, so an exception is declared
This explanation will make sense, and also relatively prove the previous guess.
The first parameter proxy in the invoke method in InvocationHandler seems to be just so that the proxy class can call a reference to its InvocationHandler object and pass in a reference to the proxy object proxyClass to do what proxyClass needs to do.

No style! Limited ability! I hope you correct me...


Related articles: