1. What is the proxy mode?
- example
Before know Java dynamic agent technology, to understand what is a proxy pattern, in fact, the proxy pattern is very common in our daily life, such as the landlord and the intermediary is actually a process of agent and the landlord have their own house, play roles in the proxy pattern is a proxy object, intermediary role is the proxy object, now have tenant want to rent a house, As intermediary agent already the landlord’s house, so tenants (client) can be directly through the intermediary agent (object) ask for house types you need (ignore the tenant to the landlord directly) and agent intermediary because the landlord house (by proxy objects), intermediary agent of many, of course, the landlord of the house, in response to different tenants (client). This process is essentially the proxy model
- conclusion
Roles in proxy mode: Proxy objects, is a proxy object, public abstract class (tenants to find house types in the mediation to have) a proxy object can agent for one or more objects (in fact, multiple objects are acting a concept of subjectivity from the dimension of interface, an object to realize multiple interfaces), the proxy pattern can play the role of an isolation The proxy pattern can be unified as is processed by a proxy object to do
- Several implementations of the proxy pattern
- Static agent
Static proxies are relatively simple to implement. First of all, we know several roles of the proxy object from above, a public abstract interface, a proxy object, and a proxied object, so we only need to record the reference of the proxied object in the proxy object
public class StudentProxy implements Person {
private Person person;
public StudentProxy(Person person) {
this.person = person;
}
@Override
public void queryScore(String courseName) { person.queryScore(courseName); }}public class Student implements Person{
private String userName;
public Student(String userName) {
this.userName = userName;
}
@Override
public void queryScore(String courseName) {
System.out.println(this.userName+"Check"+courseName+"The result"); }}public interface Person {
void queryScore(String courseName);
}
Copy the code
Summary: 1. Static proxy, simple to implement, but if there are multiple classes to be proxied, you need to create a proxy class for each proxied class, and each method to rewrite, and then call the proxied object method in the method. Proxy objects are artificially written by us. 2. Dynamic proxy
- JDK dynamic proxy
Implementation principle and technology, first of all, the JDK dynamic Proxy is based on Java reflection technology and bytecode generation technology to achieve, it is first through reflection to obtain the Proxy class method and then generate a Proxy subclass containing all the methods of the Proxy class to achieve the Proxy. The proxy objects for JDK dynamic proxies are generated dynamically rather than being coded manually
public interface Good {
void produce(a);
}
public interface Person {
void queryScore(String courseName);
}
public class Student implements Person{
private String userName;
public Student(String userName) {
this.userName = userName;
}
@Override
public void queryScore(String courseName) {
System.out.println(this.userName+"Check"+courseName+"The result"); }}public class PersonInvocation<T> implements InvocationHandler {
private T target;
private Object target2;
public PersonInvocation(T target) {
this.target = target;
}
public PersonInvocation(T target, Object target2) {
this.target = target;
this.target2 = target2;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Proxy preprocessing....");
System.out.println(proxy.getClass().getName());
Object result = method.invoke(target, args);
System.out.println("Agent post-processing.....");
returnresult; }}public class Test {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException {
Person stu1 = new Student("zs");
PersonInvocation personInvocation = new PersonInvocation<>(stu1);
// Get the proxy class generated based on the class loader and the interface setClass<? > proxyClass = Proxy.getProxyClass(Student.class.getClassLoader(),Person.class,Good.class);// Get the class constructor based on the proxy class and constructor parametersConstructor<? > constructor = proxyClass.getConstructor(InvocationHandler.class);// Execute the proxied object after being proxied
Person o = (Person) constructor.newInstance(personInvocation);
Good good = (Good) constructor.newInstance(personInvocation);
good.produce();
// The above code is equivalent to the following statement
Good proxyInstance = (Good) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class[]{Person.class,Good.class}, personInvocation);
byte[] proxyClassArr = ProxyGenerator.generateProxyClass(proxyInstance.getClass()
.getSimpleName(), proxyInstance.getClass().getInterfaces());
// Save the bytecode file to disk D named $proxy0.class
FileOutputStream outputStream = new FileOutputStream(new File(
"proxyInstance.class")); outputStream.write(proxyClassArr); outputStream.flush(); outputStream.close(); proxyInstance.produce(); }}/ / the proxy class
public final class $Proxy0 extends Proxy implements Person.Good {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
private static Method m4;
/ / to omit...
public final void gaiveMoney(a) throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw newUndeclaredThrowableException(var3); }}public final void produce(a) throws {
try {
super.h.invoke(this, m4, (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"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("learning.proxy.Person").getMethod("gaiveMoney");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
m4 = Class.forName("learning.proxy.Good").getMethod("produce");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw newNoClassDefFoundError(var3.getMessage()); }}}Copy the code
Summary: The decomcompiled implementation of the proxy class shows that it is possible to proxy multiple objects from the interface dimension, and that JDK dynamic proxies are implemented using bytecode generation and reflection techniques at run time. Source code analysis
The Proxy class@CallerSensitive
public staticClass<? > getProxyClass(ClassLoader loader, Class<? >... interfaces)throws IllegalArgumentException
{
finalClass<? >[] intfs = interfaces.clone();// Security check
final SecurityManager sm = System.getSecurityManager();
if(sm ! =null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
// The method to get the proxy class or generate the proxy based on the class loader and interface array
return getProxyClass0(loader, intfs);
}
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader, Class
[] interfaces, InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
finalClass<? >[] 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);// The previous section is consistent with the getProxyClass method
// Get the instance object of the proxy class using invocationHandler as an argument
try {
if(sm ! =null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
// Get the constituent class
finalConstructor<? > cons = cl.getConstructor(constructorParams);final InvocationHandler ih = h;
// Determine if the method is public, if not set access
if(! Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run(a) {
cons.setAccessible(true);
return null; }}); }// Create an instance of the proxy class through the constructor
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); }}// Get the proxy class from the cache, or generate it if it doesn't exist
private staticClass<? > getProxyClass0(ClassLoader loader, Class<? >... interfaces) {if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// Cache fetch
return proxyClassCache.get(loader, interfaces);
}
//private final ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>> map= new ConcurrentHashMap<>();
// Method of obtaining proxy object in WeakCache class
//key is the classloader, parameter is the interface array
public V get(K key, P parameter) {
Objects.requireNonNull(parameter);
expungeStaleEntries();
// Generate a cache key based on the class loader
Object cacheKey = CacheKey.valueOf(key, refQueue);
ValuesMap is also a map, where key is the classloader and value is a Supplier. Supplier is a consumer of Java's functional interface
// Supplier is Factory, which finds and generates the processing class for the proxy class
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 a subkey based on the classloader and interface, and obtain the supplier from valuesMap in the subkey
// subKey from valuesMap
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
Supplier<V> supplier = valuesMap.get(subKey);
Factory factory = null;
// Spin. If supplier is empty, the Facotry is generated based on the class loader interface array and key\valuesMap.
while (true) {
// If the value is not null, get the value from Facotory is the proxy object
if(supplier ! =null) {
// supplier might be a Factory or a CacheValue<V> instance
V value = supplier.get();
if(value ! =null) {
returnvalue; }}// lazy load build Factory
if (factory == null) {
factory = new Factory(key, parameter, subKey, valuesMap);
}
if (supplier == null) {
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
// successfully installed Factory
supplier = factory;
}
// else retry with winning supplier
} else {
if (valuesMap.replace(subKey, supplier, factory)) {
supplier = factory;
} else{ supplier = valuesMap.get(subKey); }}}}// The Facotry object is based on ProxyClassFactory to generate proxy classes
private static final class ProxyClassFactory
implements BiFunction<ClassLoader.Class<? > [].Class<? >>{
// The proxy class name prefix
private static final String proxyClassNamePrefix = "$Proxy";
$proxy0/ $proxy1
private static final AtomicLong nextUniqueNumber = new AtomicLong();
@Override
publicClass<? > apply(ClassLoader loader, Class<? >[] interfaces) {// Used to check whether the interface is duplicatedMap<Class<? >, Boolean> interfaceSet =new IdentityHashMap<>(interfaces.length);
// Iterate through the interface array for processing
for(Class<? > intf : interfaces) {// According to the class loader, the interface name gets the interface classClass<? > 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");
}
// Check whether it is an interface
if(! interfaceClass.isInterface()) {throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
// Check whether the interface is duplicated
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;
// Check whether non-public interfaces are in the same package. Non-public interfaces of different packages cannot be proxied
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"); }}}// Define the package name of the proxy class
if (proxyPkg == null) {
// if no non-public proxy interfaces, use com.sun.proxy package
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
// Generate proxy class bytecode based on proxy class package name and interface array
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
// Use the class loader to call the generated proxy classes into the JVM during runtime
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
throw newIllegalArgumentException(e.toString()); }}}// The proxy class is generated and written to a file
public static byte[] generateProxyClass(finalString var0, Class<? >[] var1,int var2) {
// Build the generated proxy class instance
ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
// Generate proxy class bytecode file, the main work is the interface according to the fully qualified class name to obtain method description and other information to build a method array and related information to generate a proxy class byte array stream, and then write the file
final byte[] var4 = var3.generateClassFile();
if (saveGeneratedFiles) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run(a) {
try {
int var1 = var0.lastIndexOf(46);
Path var2;
if (var1 > 0) {
Path var3 = Paths.get(var0.substring(0, var1).replace('. ', File.separatorChar));
Files.createDirectories(var3);
var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
} else {
var2 = Paths.get(var0 + ".class");
}
Files.write(var2, var4, new OpenOption[0]);
return null;
} catch (IOException var4x) {
throw new InternalError("I/O exception saving generated file: "+ var4x); }}}); }return var4;
}
Copy the code
Summary: 1. JDK dynamic proxy is actually through Java reflection to get the interface method of the proxy class and generate a new proxy class, through the class loader to generate proxy class bytecode dynamic call into the JVM to achieve dynamic proxy. JDK dynamic proxies can only be used to proxy objects based on interfaces.
2. Cglib proxy (supporting non-interface proxied objects) to be continued….