JDK dynamic proxy is widely used, Spring AOP, MyBatis mapper are used in the JDK dynamic proxy.
Use of JDK dynamic proxies
1. Create the proxy class interface and proxy class. Create a class that implements the InvocationHandler interface and implement the invoke method of that interface. 3. Create a Proxy object through the Proxy newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler H) method. Example: Create a Hello interface
public interface Hello {
void sayHello(a);
}
Copy the code
Create an implementation class that implements the Hello interface.
public class HelloImpl implements Hello {
public void sayHello(a) {
System.out.println("hello"); }}Copy the code
Create a class that implements the InvocationHandler interface.
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class HelloInvocationHandler implements InvocationHandler {
private Object hello;
public HelloInvocationHandler(Object hello) {
this.hello = hello;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("start");
Object invoke = method.invoke(hello, args);
System.out.println("end");
returninvoke; }}Copy the code
Create a Proxy object using Proxy
Hello hello = new HelloImpl();
HelloInvocationHandler helloInvocationHandler = newHelloInvocationHandler(hello); ClassLoader classLoader = hello.getClass().getClassLoader(); Class<? >[] interfaces = hello.getClass().getInterfaces(); Hello helloProxy = (Hello)Proxy.newProxyInstance(classLoader, interfaces, helloInvocationHandler); helloProxy.sayHello();Copy the code
Take a look at the output
start
hello
end
Copy the code
The source code
Take a look at the Proxy’s newProxyInstance method
public static Object newProxyInstance(ClassLoader loader, Class
[] interfaces, InvocationHandler h)
throws IllegalArgumentException
{
/ / to omit
/* * Generate the proxy class */Class<? > cl = getProxyClass0(loader, intfs);/* * Invoke its constructor with the designated invocation handler. */
try {
/ / to omit
// Get the proxy class constructor
finalConstructor<? > cons = cl.getConstructor(constructorParams);final InvocationHandler ih = h;
if(! Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run(a) {
cons.setAccessible(true);
return null; }}); }// Through the constructor, set InvocationHandler to the proxy object and create the proxy object
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw newInternalError(t.toString(), t); }}catch (NoSuchMethodException e) {
throw newInternalError(e.toString(), e); }}Copy the code
The main function of this code is to generate the proxy class. The constructor of the proxy class creates a proxy object. How to generate the proxy class
private staticClass<? > getProxyClass0(ClassLoader loader, Class<? >... interfaces) {if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
return proxyClassCache.get(loader, interfaces);
}
Copy the code
GetProxyClass0 determines the number of interfaces, and then gets the proxy class from proxyClassCache. Take a look at the definition of proxyClassCache
private static finalWeakCache<ClassLoader, Class<? >[], Class<? >> proxyClassCache =new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
Copy the code
By definition, proxyClassCache is actually a cache class, and the generated class is placed in the cache. Let’s look at a constructor for this class
public WeakCache(BiFunction
subKeyFactory, BiFunction
valueFactory)
,>
,> {
this.subKeyFactory = Objects.requireNonNull(subKeyFactory);
this.valueFactory = Objects.requireNonNull(valueFactory);
}
Copy the code
You can see that subKeyFactory is actually KeyFactory and valueFactory is actually ProxyClassFactory
Okay, now back to the getProxyClass0 method, which analyzed the proxyClassCache, let’s move on to its get method
public V get(K key, P parameter) { // key is ClassLoader interface
/ / to omit
// Obtain cacheKey according to ClassLoader
Object cacheKey = CacheKey.valueOf(key, refQueue);
ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
if (valuesMap == null) {
ConcurrentMap<Object, Supplier<V>> oldValuesMap
= map.putIfAbsent(cacheKey,
valuesMap = new ConcurrentHashMap<>());
if(oldValuesMap ! =null) { valuesMap = oldValuesMap; }}// Generate subkeys based on keys and interfaces
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
Supplier<V> supplier = valuesMap.get(subKey);
Factory factory = null;
while (true) {
if(supplier ! =null) {
V value = supplier.get();
if(value ! =null) {
returnvalue; }}if (factory == null) {
factory = new Factory(key, parameter, subKey, valuesMap);
}
if (supplier == null) {
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) { supplier = factory; }}else {
if (valuesMap.replace(subKey, supplier, factory)) {
supplier = factory;
} else{ supplier = valuesMap.get(subKey); }}}}Copy the code
Retrun value returns a proxy class. Retrun value returns a proxy class. Retrun value returns a proxy class. Supplier is Factory; Factory is generated by key, paramter, subkey, valuesMap. Key and paramter are method inputs classLoader and interfaces. Subkeys are derived from subKeyFactory and paramter keys, i.e. from classLoaders and interfaces; A vlauesMap is obtained by a map based on a cacheKey, which is obtained by a key based on a classloader. Finally, this method is used to obtain the supplier from the map, but the map structure is complicated. It is a two-tier map structure: The first level key is a cacheKey object generated based on the ClassLoader, and the second level key is a Subkey object generated by the classLoader and interfaces.
Next, go to supplene.get () to see how it generates the proxy object. Supplier is a Factory, so look at the Factory get method
@Override
public synchronized V get(a) { // serialize access
Supplier<V> supplier = valuesMap.get(subKey);
if(supplier ! =this) {
return null;
}
V value = null;
try {
// Get the proxy class
value = Objects.requireNonNull(valueFactory.apply(key, parameter));
} finally {
if (value == null) { // remove us on failure
valuesMap.remove(subKey, this); }}assertvalue ! =null;
CacheValue<V> cacheValue = new CacheValue<>(value);
reverseMap.put(cacheValue, Boolean.TRUE);
if(! valuesMap.replace(subKey,this, cacheValue)) {
throw new AssertionError("Should not reach here");
}
returnvalue; }}Copy the code
Valuefactory. apply(key,paramter) is a ProxyClassFactory
@Override
publicClass<? > apply(ClassLoader loader, Class<? >[] interfaces) {// Interface validation is omitted
String proxyPkg = null; // package to define proxy class in
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
// Verify the interface is omitted
if (proxyPkg == null) {
// if no non-public proxy interfaces, use com.sun.proxy package
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
// Assemble the generated class name
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
// Get the class's byte array
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
throw newIllegalArgumentException(e.toString()); }}}Copy the code
Finally, the proxy class is generated by ProxyGenerator, the specific generation details will not be analyzed, here involves a lot of Java class bytecode knowledge, in ProxyGenerator also reflected a lot of bytecode things, interested can study, Google.
Now that you have the byte array, you can save it as a class and decompile to see the generated proxy class code.
// The proxy class name
String className = "HelloProxy";
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
// Get the proxy class byte array
byte[] data = ProxyGenerator.generateProxyClass(className, new Class[]{Hello.class},accessFlags);
FileOutputStream out;
out = null;
try {
// Save the proxy class to a file
out = new FileOutputStream(className + ".class");
System.out.println((new File("HelloProxy")).getAbsolutePath());
out.write(data);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (null! = out) {try {
out.close();
} catch(IOException e) { e.printStackTrace(); }}}Copy the code
Decompile-generated proxy classes:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class HelloProxy extends Proxy implements Hello {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public Hello(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw newUndeclaredThrowableException(var4); }}public final void sayHello(a) throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw newUndeclaredThrowableException(var3); }}public final String toString(a) throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw newUndeclaredThrowableException(var3); }}public final int hashCode(a) throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw newUndeclaredThrowableException(var3); }}static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("Bolg.Hello").getMethod("sayHello");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw newNoClassDefFoundError(var3.getMessage()); }}}Copy the code
The proxy class gets all the propped object’s methods reflectively. When calling a method, it actually calls the Invoke method of the InvocationHandler passed in from the constructor and passes in the propped object’s methods.