Hello ~~ bozos, xiao recently looked at the source code of Spring, happened to encounter several Aop problems involving Java dynamic proxy, before this thing can roughly understand, but did not carefully look at the source code, today we pick a pick its true face.
Read this article and you will learn:
-
Understand why Jdk dynamic proxies, proxy classes must implement interfaces
-
Understand the whole process of Jdk dynamic proxy
-
Looking at the Jdk’s dynamic proxy classes for class content, go straight to last “/” if you want to see only this one
-
How do I get the contents of a proxy class myself
-
How to read source code
-
stronger
Same old rule — code the phenomenon
public interface HelloService {
void sayHello(a);
}
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
public class HelloServiceImpl implements HelloService {
@Override
public void sayHello(a) {
System.out.println("Bow down to the Nuggets."); }}/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
public class HelloInvocationHandle implements InvocationHandler {
private Object target;
public HelloInvocationHandle(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("proxy before=======");
Object result = method.invoke(target, args);
System.out.println("proxy end=======");
returnresult; }}Copy the code
The above code, I believe you are learning dynamic proxy, have written, I will not repeat, you have thought about how he is implemented? Why is it that the invoke hello () logic of the invoke proxy class can be used directly by invoking the Invoke () logic of InvocationHandler? Why is it so hard for a proxy class to have to implement an interface? Let’s move on.
Source code analysis
If we look at the Main method above, we can see that proxy.newProxyInstance () is the method that carries all the logic of the Proxy class, all the magic in it
HelloService Proxy = (HelloService) proxy.newProxyInstance (thread.currentThread ().getContextClassLoader(), hello.getClass().getInterfaces(),handle);Copy the code
Let’s click inside and see what it looks like
private static finalClass<? >[] constructorParams = { InvocationHandler.class };public static Object newProxyInstance(ClassLoader loader, Class
[] interfaces, InvocationHandler h) {...// The interface array contains HelloService, which is the interface that the proxy class needs to implement
// There is a copy waiting to be processed
finalClass<? >[] intfs = interfaces.clone(); .// Get the proxy ClassClass<? > cl = getProxyClass0(loader, intfs); .// Pass in the constructor object to get the constructor for the proxy class
// From this we can guess that the proxy class has a constructor that is initialized by passing in InvocationHandler
finalConstructor<? > cons = cl.getConstructor(constructorParams);// The constructor instantiates the proxy object by passing in the HelloInvocationHandle we defined earlier by reflection
return cons.newInstance(new Object[]{h});
Copy the code
Simple analysis of the above code, the process is also very simple
- The incoming
HelloService.class
Class generates the proxy class, which has method information from the original class, for examplesayHello()
- Through a
InvocationHandler
The constructor gets the constructor of the proxy class - We defined it before we passed it in
HelloInvocationHandle
The constructor instantiates a live proxy object and we get the general idea hereJdk
Principle of dynamic proxy:Clone an interface and generate a new class as the proxy class, which has the HelloInvocationHandle we defined to handle the proxy logic
The Jdk dynamic proxy class must implement an interface because its implementation dictates that it must pass an interface to generate the proxy class
So the whole mystery lies in the method getProxyClass0(loader, INTFS) that generates the proxy class
private staticClass<? > getProxyClass0(ClassLoader loader, Class<? >... interfaces) { ...// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
// The proxy is generated from the Cache of a proxy Cache. It is beyond the scope of our discussion to see how this class is generated
return proxyClassCache.get(loader, interfaces);
}
// From the comments above and here we can see that the proxy class is generated from a class called ProxyClassFactory
private static finalWeakCache<ClassLoader, Class<? >[], Class<? >> proxyClassCache =new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
Copy the code
ProxyClassFactory is responsible for generating the proxy class
I am looking at jdK1.8 source code, using lambda reconstruction, the old version can see different, the main logic should not change
// prefix for all proxy class names
private static final String proxyClassNamePrefix = "$Proxy"; public Class<? > apply(ClassLoader loader, Class<? >[] interfaces) { ... // The name of the proxy class is defined here, so every time you see the proxy class is$ProxyProxyName = proxyPkg + proxyClassNamePrefix + num; / / key points here, here to generate a proxy class byte [] proxyClassFile = ProxyGenerator. GenerateProxyClass (proxyName, interfaces, accessFlags); }Copy the code
ProxyGenerator. GenerateProxyClass (proxyName, interfaces, accessFlags) this method if you are interested in big points in as you can see, it is to use StringBuilder spell the proxy class bytecode (: , and converted to a Byte array.
Bingo
Here’s the key! What does this proxy class look like? [] proxyClassFile [] proxyClassFile [] proxyClassFile [] proxyClassFile [] proxyClassFile That’s right! Output stream wait! Small one ton operation, QWER! Fly up
Oh..
Decompilation, uh-huh
public final class $Proxy0extends Proxy implements HelloService { private static Method m1; //equals() private static Method m3; //Bingo we sayHello Method () private static Method m2; //toString() private static Method m0; //hashCode() method // here is the InvocationHandler constructor we submitted earlier!! public$Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue(); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); }} // Come warriors! Take a look at the sayHello() method of our proxy class! public final void sayHello() throws { try { super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final String toString() throws { try {return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
m3 = Class.forName("proxy.service.HelloService").getMethod("sayHello", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); }}}Copy the code
Focus on the sayHello() method of the proxy class above, folks. Is the truth out? The invocation invocation class implements the invoke() method from the interface.
Super.hinvoke (this, m3, (Object[])null); What is super. H in the thing, the discerning big man should know? It inherits the Proxy class, which is the InvocationHandler variable defined in Proxy, your HelloInvocation
conclusion
Don’t you think Jdk dynamic proxy is too complicated? Like you could write it yourself? Haha, this article also want to provide a Debug code ideas, actually look at the source code is not that difficult, I hope to help you, we will see you next time ~
By the way, HERE’s my Github: Click on a few of the things I’m currently working on:
Triple is an RPC framework written by himself. The function is not very perfect, and the main purpose is to understand netty-related knowledge points with the help of this framework
Data structure and algorithm data structure and algorithm is weak, learn to practice good blow 🐂🍺
Spring source analysis is still in the analysis, which will sort out some problems, with the problem to see the source code more efficient, welcome what you do not understand about Spring, you can give me the Issue, the article written should also be sent to nuggets, after all, gay men (escape.