Dynamic proxies in the JDK are used to create dynamic proxies. Start by defining an interface
public interface Man {
void findObject()throws Throwable;
}
Copy the code
2. Define an implementation class
public class Zhangsan implements Man {
@Override
public void findObject() {
System.out.println("oh , i find you."); }}Copy the code
- Define InvocationHandler
public class JdkHandler implements InvocationHandler {
private Object target;
JdkHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before");
method.invoke(target,null);
System.out.println("before");
returnnull; }}Copy the code
- Simple test Demo:
public class TestMain {
public static void main(String[] args) throws Throwable{
System.setProperty("dk.proxy.ProxyGenerator.saveGeneratedFiles".true); Man man = new Zhangsan(); InvocationHandler handler = new JdkHandler(man); Man man1 = (Man) Proxy.newProxyInstance(TestMain.class.getClassLoader(),new Class[]{Man.class},handler); man1.findObject(); }}Copy the code
$Proxy0. Class = com.sun.proxy; $Proxy0. Class = com.sun.proxy; The dynamic proxy class for the interface is then passed in to the implementation class of InvocationHandler to implement the proxy for the interface
final class $Proxy0 extends Proxy implements Man {
private static Method m0;
private static Method m1;
private static Method m2;
private static Method m3;
public $Proxy0(InvocationHandler param1) {
super(var1);
}
public final int hashCode() {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final boolean equals(Object var1) {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() {
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 void findObject() throws Throwable {
super.h.invoke(this, m3, (Object[])null);
}
static {
try {
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("com.jdk14.demo.dynamic.myjdk.Man").getMethod("findObject"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); }}}Copy the code
Get the constructor of the Proxy class, and then use newProxyInstance to generate the instance of the Proxy class. This function is more complicated to get the instance, and will be described in more detail below.
public static Object newProxyInstance(ClassLoader loader, Class<? >[] interfaces, InvocationHandler h) { Objects.requireNonNull(h); final Class<? >caller= System.getSecurityManager() == null ? null: Reflection.getCallerClass(); /* * Look up or generate the designated proxy class and its constructor. */ Constructor<? > cons = getProxyConstructor(caller, loader, interfaces);
return newProxyInstance(caller, cons, h);
}
Copy the code
The JDK supports multiple interfaces and a single interface. Use ClassLoaderValue<Constructor<? >> proxyCache = new ClassLoaderValue<>();
private static Constructor<? > getProxyConstructor(Class<? >caller,ClassLoader loader, Class<? >... interfaces){ // optimizationfor single interface
if(interfaces.length == 1) { Class<? > intf = interfaces[0];if (caller! = null) { checkProxyAccess(caller, loader, intf);
}
return proxyCache.sub(intf).computeIfAbsent(
loader,
(ld, clv) -> new ProxyBuilder(ld, clv.key()).build()
);
} else{ // interfaces cloned final Class<? >[] intfsArray = interfaces.clone();if (caller! = null) { checkProxyAccess(caller, loader, intfsArray); } final List<Class<? >> intfs = Arrays.asList(intfsArray);returnproxyCache.sub(intfs).computeIfAbsent( loader, (ld, clv) -> new ProxyBuilder(ld, clv.key()).build() ); }}Copy the code
The proxy Class is created using the ProxyBuilder#build method. The most important thing here is that the defineProxyClass method returns the produced proxy Class.
ProxyBuilder(ClassLoader loader, Class<? > intf){ this(loader, Collections.singletonList(intf)); } Constructor<? >build() { Class<? > proxyClass = defineProxyClass(module, interfaces); final Constructor<? > cons; try { cons = proxyClass.getConstructor(constructorParams); } catch (NoSuchMethodException e) { throw new InternalError(e.toString(), e); } AccessController.doPrivileged(new PrivilegedAction<Void>() { public Voidrun() {
cons.setAccessible(true);
returnnull; }});return cons;
}
Copy the code
Generate the proxy class using the Proxy#defineProxyClass method. The main steps are:
- Determines whether the access attribute of the interface is public
- Proxy class name generation, package name +$Proxy+ increment variable,
- Then get the class loaded, generate bytecode through ProxyGenerator#generateProxyClass method,(the underlying use of ASM to generate bytecode) 4. The bytecode is loaded via the ClassLoader#defineClass method and the JVM returns the corresponding CLass 5, the tag cache is true and there is an important note here that there is a tag in the ProxyGenerator, JDK. Proxy. ProxyGenerator. SaveGeneratedFiles key can open the generated proxy files saved to a file, the default path is com. The sun. The proxy directory,
private static Class<? > defineProxyClass(Module m, List<Class<? >> interfaces) { String proxyPkg = null; // package to define proxy classin
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
/*
* Record the package of a non-public proxy interface so that the
* proxy class will be defined in the same package. Verify that
* all non-public proxy interfaces are in the same package.
*/
for(Class<? > intf : interfaces) { int flags = intf.getModifiers();if(! Modifier.isPublic(flags)) { accessFlags = Modifier.FINAL; // non-public, final String pkg = intf.getPackageName();if (proxyPkg == null) {
proxyPkg = pkg;
} else if(! pkg.equals(proxyPkg)) { throw new IllegalArgumentException("non-public interfaces from different packages"); }}}if (proxyPkg == null) {
// all proxy interfaces are public
proxyPkg = m.isNamed() ? PROXY_PACKAGE_PREFIX + "." + m.getName()
: PROXY_PACKAGE_PREFIX;
} else if (proxyPkg.isEmpty() && m.isNamed()) {
throw new IllegalArgumentException(
"Unnamed package cannot be added to " + m);
}
if (m.isNamed()) {
if(! m.getDescriptor().packages().contains(proxyPkg)) { throw new InternalError(proxyPkg +" not exist in " + m.getName());
}
}
/*
Choose a name for the proxy class to generate.*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg.isEmpty()
? proxyClassNamePrefix + num
: proxyPkg + "."+ proxyClassNamePrefix + num; ClassLoader loader = getLoader(m); trace(proxyName, m, loader, interfaces); /* * Generate the specified proxy class. */ byte[] proxyClassFile = PROXY_GENERATOR_V49 ? ProxyGenerator_v49.generateProxyClass(proxyName, interfaces, accessFlags) : ProxyGenerator.generateProxyClass(loader, proxyName, interfaces, accessFlags); try { Class<? > pc = JLA.defineClass(loader, proxyName, proxyClassFile, null,"__dynamic_proxy__");
reverseProxyCache.sub(pc).putIfAbsent(loader, Boolean.TRUE);
return pc;
} catch (ClassFormatError e) {
/*
* A ClassFormatError here means that (barring bugs inthe * proxy class generation code) there was some other * invalid aspect of the arguments supplied to the proxy * class creation (such as virtual machine limitations * exceeded). */ throw new IllegalArgumentException(e.toString()); }}Copy the code
$Proxy0 = $Proxy0 = $Proxy0 = $Proxy0 In the proxy class, the Method of Ojbect’s hashCode,equals, and toString are initially obtained by reflection. Then the Method of the interface is obtained. The cached property fields of the proxy class are implemented by passing in InvocationHandler. AOP can then be implemented by reflecting the call through Method.invoke and adding processing logic before and after the call
Finally, a diagram summarizes the context of proxy generation:
Conclusion: Today we mainly talk about the JDK dynamic proxy generation process analysis, In fact, there is another class proxy, this is to make up for the lack of JDK implementation of class proxy, in fact, the JDK implementation and cglib implementation of the underlying implementation of cglib, so from this point of view, both performance is similar, in fact, as far as cglib is concerned, It can proxy both interfaces and classes, so cglib is comprehensive, but mainstream frameworks like Spring have classes that use Cglib proxies if the interface proxy still uses JDK proxies in preference.