Article Directory
1. Agency Model
代理模式
Is one of the commonly used design patterns, its characteristic isThe proxy class has the same interface as the proxy class. The proxy class can perform pre- and post-processing for the execution of the proxy class method to enhance the proxy class method
The class structure of the proxy model is usually as shown in the figure above. There will be an association relationship between the proxy class and the proxy class. An object of the proxy class holds an object of the proxy class. The object of the proxy class itself does not really implement the service, but provides specific services by calling the relevant methods of the proxy class object
2. Dynamic proxy usage
The proxy class is not defined in the Java code, but is dynamically generated at runtime according to the "instructions" in the Java code ( 字节码由JVM在运行时动态生成而非预存在任何一个 .class 文件中
),This proxy method of creating proxy classes while the program is running is called动态代理
, Its advantage is that it can conveniently process the functions of the proxy class. This is because all the methods executed by the proxy are InvocationHandler#invoke()
called through methods, which is equivalent to putting a shell on all methods of the proxy class, so as long as this method is processed uniformly, all the proxy methods can be the same Operated
The following code shows the simple use of dynamic proxy, the basic steps are as follows:
- Define a public interface, in this case
IHello
, there is an abstract method in the interface- Entity class defines implements a common interface as the proxy class, the present embodiment is the proxy class
Hello
implementsIHello
the interface, the abstract method override interface- Defines implements a
InvocationHandler
method interception class interface, rewritinginvoke()
method to implement the processing logic interception proxy class is executed when the method of- By
Proxy.newProxyInstance()
generating a proxy object methods, the implementation of an interface method after the proxy object can hold
public class ServiceProxy {
public interface IHello {
String sayHi();
}
public static class Hello implements IHello {
@Override
public String sayHi() {
return "Hello";
}
}
// 动态代理类
public static class ProxyHandler<T> implements InvocationHandler {
private T origin;
public ProxyHandler(T origin) {
this.origin = origin;
}
/**
* @param o 代理对象引用
* @param method 正在执行目标的方法
* @param objects 目标方法执行时的入参
*/
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
String s = "proxy";
s += method.invoke(origin, objects);
return s;
}
}
public static void main(String[] args) {
IHello IHello = (IHello) getInstance(IHello.class, new ProxyHandler<>(new Hello()));
System.out.println(IHello.toString());
generateProxyClass();
}
// 创建代理对象
public static <T> Object getInstance(Class<T> clazz, ProxyHandler<T> handler) {
return Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{
clazz}, handler);
}
private static void generateProxyClass() {
byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", Hello.class.getInterfaces());
String path = "/Users/nathan.yang/workspace/algorithm_Java/out/StuProxy.class";
try (FileOutputStream fos = new FileOutputStream(path)) {
fos.write(classFile);
fos.flush();
System.out.println("代理类文件写入成功");
} catch (Exception e) {
System.out.println("写文件错误");
}
}
}
3. The principle of dynamic agency
-
Proxy#newProxyInstance()
The method is the entrance of the dynamic proxy. The main steps to generate dynamic proxy objects are as follows:getProxyClass0()
Method to generate proxy class- After obtaining the proxy class
InvocationHandler
objects into the reference reflective call the constructor to generate dynamic proxy object
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { Objects.requireNonNull(h); final Class<?>[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } /* * Look up or generate the designated proxy class. */ Class<?> cl = getProxyClass0(loader, intfs); /* * Invoke its constructor with the designated invocation handler. */ try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { cons.setAccessible(true); return null; } }); } 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 new InternalError(t.toString(), t); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString(), e); } }
-
Proxy#getProxyClass0()
In fact, from a methodWeakCache
to obtain the proxy class that acquires the logic is that if there is no proxy cache class that kind of thing is calledProxyClassFactory#apply()
, to generate a proxy class instant by proxy class factory, the following steps:- First verify whether the target interface can be loaded by the specified class loader
- Determine the package where the proxy class is located and the fully qualified name of the proxy class based on conditions such as the package where the interface is located. The proxy class name is
包名+$Proxy+id
- By
ProxyGenerator.generateProxyClass()
generating a bytecode array to dynamically generate and load the proxy class
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) { Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length); for (Class<?> intf : interfaces) { /* * Verify that the class loader resolves the name of this * interface to the same Class object. */ Class<?> interfaceClass = null; try { interfaceClass = Class.forName(intf.getName(), false, loader); } catch (ClassNotFoundException e) { } if (interfaceClass != intf) { throw new IllegalArgumentException( intf + " is not visible from class loader"); } /* * Verify that the Class object actually represents an * interface. */ if (!interfaceClass.isInterface()) { throw new IllegalArgumentException( interfaceClass.getName() + " is not an interface"); } /* * Verify that this interface is not a duplicate. */ if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) { throw new IllegalArgumentException( "repeated interface: " + interfaceClass.getName()); } } String proxyPkg = null; // package to define proxy class in 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; String name = intf.getName(); int n = name.lastIndexOf('.'); String pkg = ((n == -1) ? "" : name.substring(0, n + 1)); if (proxyPkg == null) { proxyPkg = pkg; } else if (!pkg.equals(proxyPkg)) { throw new IllegalArgumentException( "non-public interfaces from different packages"); } } } if (proxyPkg == null) { // if no non-public proxy interfaces, use com.sun.proxy package proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; } /* * Choose a name for the proxy class to generate. */ long num = nextUniqueNumber.getAndIncrement(); String proxyName = proxyPkg + proxyClassNamePrefix + num; /* * Generate the specified proxy class. */ byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); try { return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { /* * A ClassFormatError here means that (barring bugs in the * 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()); } }
-
Reflection parameter acquired proxy class
InvocationHandler.class
constructor, in fact, isProxy
the parameterized constructor, call the constructorcons.newInstance(new Object[]{h})
to generate a proxy objectprotected Proxy(InvocationHandler h) { Objects.requireNonNull(h); this.h = h; }
-
The following code can output the proxy class loaded in the JVM into a class file, and then you can use the decompiler tool to view the source code of the proxy class
private static void generateProxyClass() { byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", Hello.class.getInterfaces()); String path = "/Users/nathan/workspace/algorithm_Java/out/StuProxy.class"; try (FileOutputStream fos = new FileOutputStream(path)) { fos.write(classFile); fos.flush(); System.out.println("代理类文件写入成功"); } catch (Exception e) { System.out.println("写文件错误"); } }
-
The generated proxy class source code is as follows, it is obvious that you can see the principle of dynamic proxy implementation of this class:
- By
static
code blockProxy classEach package in the method ofMethod
the object, the table generation method - Proxy objectExecutes proxy class method with the same name, parent class by
Proxy
pointing to retainInvocationHandler
an object reference to callInvocationHandler#invoke()
a method, complete dynamic proxies
public final class $Proxy0 extends Proxy implements IHello { private static Method m1; private static Method m3; private static Method m2; private static Method m0; public $Proxy0(InvocationHandler var1) throws { super(var1); } public final String sayHi() throws { try { // 父类 Proxy 保留的指向 InvocationHandler 对象的引用调用 invoke() 方法 return (String)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); } } ...... // 方法表 static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m3 = Class.forName("ServiceProxy$IHello").getMethod("sayHi"); 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 new NoClassDefFoundError(var3.getMessage()); } } }
- By