By chance today, I suddenly wanted to take a look at the dynamic proxy of JDK, because I knew a little about it before, and I just wanted to test its use. I quickly wrote these interfaces and classes:
Interface class: UserService.java
Copy the code code as follows:
package com.yixi.proxy;
public interface UserService {
public int save();
public void update(int id);
}
Implementation class: UserServiceImpl.java
Copy the code code as follows:
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 anxiously wrote the InvocationHandler I wanted: the function of this is very simple, it is to record the start time and end time of method execution.
TimeInvocationHandler.java
Copy the code code as follows:
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;
}
}
After all the preparations are done, of course it’s time to start writing tests!
Test.java
Copy the code code as follows:
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 ran happily, but it didn't give you any face. The result was an exception that filled the screen:
Copy the code code as follows:
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
The com.yixi.proxy.TimeInvocationHandler.invoke(TimeInvocationHandler.java:12) exception clearly tells the problem in line 12 of TimeInvocationHandle: that is
Copy the code code as follows:
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 invoke() method seems to provide all the parameters required by method.invoke(Object, Object[]), we will use it as a matter of course. If you really think that way, then you have fooled the JDK. It’s a trap. Let’s look at the correct way to write it first. In case some students are not in the mood to read the following, at least give me the correct solution:
Modify TimeInvocationHandler.java
Copy the code code as follows:
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 Test.java
Copy the code code as follows:
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 here is the correct output:
Copy the code code as follows:
startTime: 1352879531334
update a user 2
endTime: 1352879531334
startTime: 1352879531334
user save....
endTime: 1352879531335
If you want less code, you can write an anonymous class directly:
package com.yixi.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import 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.out.println("startTime : " +System.currentTimeMillis());
Object obj = method.invoke(usi, args);
System.out.println("endTime : " +System.currentTimeMillis());
return obj;
}
});
u.update(2);
u.save();
}
}
Since the first parameter in method.invoke(target,args); is the target object, why does the Invoke method of invocationHandler need an Object proxy parameter? Let’s look down!
For the most important invoke method (in my personal opinion) let’s take a look at what the JDK says:
Copy the code code as follows:
invoke
Object invoke(Object proxy,
Method method,
Object[] args)
throws Throwable handles method calls on the proxy instance and returns the result. This method is called on the invocation handler when the method is called on the proxy instance it is associated with.
parameter:
proxy - the proxy instance on which the method is called
method - a Method instance corresponding to the interface method called on the proxy instance. The declaring class of a Method object will be the interface in which the method is declared, which may be a superinterface of the proxy interface from which the proxy class inherits the method.
args - an array of objects containing the argument values passed into the method call on the proxy instance, or null if the interface method takes no arguments. Parameters of basic types are wrapped in instances of the appropriate basic wrapper class (such as java.lang.Integer or java.lang.Boolean).
proxy - the proxy instance on which the method is called? What does this sentence mean? acting? method is the proxy method? Then shouldn't my method for executing the proxy be Object obj = method.invoke(proxy, args);? I didn't turn around at that time. I went to the discussion group and went to Google but couldn't find any inspiration. I thought I'd better look at the source code and maybe I could see something!
Open the source code of the Proxy class and find out what a constructor is:
Copy the code code as follows:
protectedInvocationHandler h;
protected Proxy(InvocationHandler h) {
this.h = h;
}
Use InvocationHandler as a parameter of Proxy's constructor.... Then what does it use InvocationHandler for? Is there any connection with the invoke() method in InvocationHandler?
My first thought is that Proxy will call the following statement internally:
Copy the code code as follows:
h.invoke(this,methodName,args);
Because you have to call the invoke method to execute the corresponding method.
Let's take a look at this first
Here you will find something that seems to make sense: when u.update(2), àProxy will call handler.invoke(proxyClass,update,2) à, which means proxyClass.update(2);
When u.save(); àProxy will call handler.invoke(proxyClass,save,null) àthat is, proxyClass.save();
When Test.java is changed to this:
Copy the code code as follows:
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();
}
}
Note that the method of the anonymous class at this time returns null. If you run it, you will find:
Copy the code code as follows:
Exception in thread "main" java.lang.NullPointerException
at $Proxy0.save(Unknown Source)
at com.yixi.proxy.Test.main(Test.java:17)
There is a null pointer in line 17, that is, the u.save() method here has a null element. Could it be that u is empty? It shouldn't be. If u is null, then u.update(2) will report a null pointer exception there. When I comment out line 17, the exception disappears, indicating that u.update() can execute normally. So why is this?
In fact, this is why the invoke method returns null:
Pay attention to the two methods in the UserService class:
Copy the code code as follows:
public interface UserService {
public int save();
public void update(int id);
}
The Save() method returns an int type and the update method returns a void type; according to the above guess, handler.invoke() implements proxyClass.update(2);, and the return method in the invoke method is corresponding The return value of the proxy method,
So when the invoke method returns null, the agent's update method receives a return value of null, and it originally returns void, so no exception is reported, and the agent save must return an int type value. What we return here is still null, and the JVM cannot convert null. It is converted into int type, so an exception is reported. This explanation can be explained clearly, and it can also relatively prove the previous guess.
The first parameter proxy in the invoke method of InvocationHandler seems to be just to allow the Proxy class to pass in the reference of the proxy object proxyClass when calling the method with the reference of its own InvocationHandler object to complete the business that proxyClass needs to complete.
Literary talent is not good! Ability is limited! I hope you guys can correct me...