In depth analysis of static and dynamic proxies in Java
- 2020-04-01 02:18:42
- OfStack
Proxy is often used in Java coding. Proxy is divided into static proxy and dynamic proxy. Where the dynamic proxy can implement aop in spring.
I. static agent: Before the program runs, the programmer writes the proxy and then compiles it, that is, the bytecode file of the proxy class is generated before the program runs
The public parent of the proxied class
package staticproxy;
public abstract class BaseClass {
public abstract void add();
}
By the proxy class
package staticproxy;
public class A extends BaseClass {
public void add() {
System.out.println("A add !");
}
}
The proxy class
package staticproxy;
public class Proxy {
BaseClass baseClass;
public void add() {
baseClass.add();
}
public void setBaseClass(BaseClass baseClass) {
this.baseClass = baseClass;
}
public static void main(String[] args) {
BaseClass baseClass = new A();
Proxy proxy = new Proxy();
proxy.setBaseClass(baseClass);
proxy.add();
}
}
Ii. Dynamic agency The actual code is not generated at compile time, but is generated dynamically at run time using reflection
Interface of the proxied class
package jdkproxy;
public interface Service {
public void add();
public void update();
}
Proxied class A
package jdkproxy;
public class AService implements Service {
public void add() {
System.out.println("AService add>>>>>>>>>>>>>>>>>>");
}
public void update() {
System.out.println("AService update>>>>>>>>>>>>>>>");
}
}
Proxied class B
package jdkproxy;
public class BService implements Service {
public void add() {
System.out.println("BService add---------------");
}
public void update() {
System.out.println("BService update---------------");
}
}
The proxy class
package jdkproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler {
private Object target;
MyInvocationHandler() {
super();
}
MyInvocationHandler(Object target) {
super();
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//Add logic before program execution
System.out.println("before-----------------------------");
//Program execution
Object result = method.invoke(target, args);
//Add logic after program execution
System.out.println("after------------------------------");
return result;
}
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
}
The test class
package jdkproxy;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
Service aService = new AService();
MyInvocationHandler handler = new MyInvocationHandler(aService);
//Proxy dynamically creates a Proxy instance that conforms to an interface for the InvocationHandler implementation class
Service aServiceProxy = (Service) Proxy.newProxyInstance(aService
.getClass().getClassLoader(), aService.getClass()
.getInterfaces(), handler);
//Dynamically generated proxy object to aServiceProxy agent to execute the program, where aServiceProxy conforms to the Service interface
aServiceProxy.add();
System.out.println();
aServiceProxy.update();
//The following is the agent for B
// Service bService = new BService();
// MyInvocationHandler handler = new MyInvocationHandler(bService);
// Service bServiceProxy = (Service) Proxy.newProxyInstance(bService
// .getClass().getClassLoader(), bService.getClass()
// .getInterfaces(), handler);
// bServiceProxy.add();
// System.out.println();
// bServiceProxy.update();
}
}
Output results:
Before -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
AService add> > > > > > > > > > > > > > > > > >
After -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
Before -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
AService update> > > > > > > > > > > > > > >
After -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
With the above standard red statement is the key to produce the proxy class code, can produce a proxy object, in line with the Service interface newProxyInstance this method would do such a thing, he will take you to all the interface agent, with a class that is dynamically generated by the code to do this, all the interface methods in the class rewritten as call InvocationHandler. Invoke () method.
How is the proxy object generation implemented in detail
The newProxyInstance method of the Proxy, where the exception handling statements in the method have been truncated for convenience
Public static Object newProxyInstance(ClassLoader loader, Class< ? > [] interfaces, throws InvocationHandler h)
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h) throws IllegalArgumentException
{
if (h == null) {
throw new NullPointerException();
}
//Generates the specified proxy class
Class cl = getProxyClass(loader, interfaces);
Constructor cons = cl.getConstructor(constructorParams);
//Generate an instance of the proxy class and pass an instance of MyInvocationHandler to its constructor. The proxy class object will call the invoke method of MyInvocationHandler when it is actually executed.
return (Object) cons.newInstance(new Object[] { h });
} Among them getProxyClass Method returns an instance of the proxy class
Proxy's getProxyClass method
public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) throws IllegalArgumentException
{
//Much of the caching, exception handling, and judgment logic code has been omitted to make the program more prominent
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);
proxyClass = defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);
proxyClasses.put(proxyClass, null);
return proxyClass;
}
Next, look at the generateProxyClass method of ProxyGenerator, which finally produces the bytecode file of the proxy class:
public static byte[] generateProxyClass(final String name, Class[] interfaces)
{
ProxyGenerator gen = new ProxyGenerator(name, interfaces);
//Here, the bytecode of the proxy class is dynamically generated
final byte[] classFile = gen.generateClassFile();
//If the value of saveGeneratedFiles is true, the bytecode of the generated proxy class is saved to the hard disk & PI.
if (saveGeneratedFiles) {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
try {
FileOutputStream file =
new FileOutputStream(dotToSlash(name) + ".class");
file.write(classFile);
file.close();
return null;
} catch (IOException e) {
throw new InternalError(
"I/O exception saving generated file: " + e);
}
}
});
}
//Returns the bytecode & NBSP of the proxy class;
return classFile;
}
So what does the resulting proxy class look like, as follows (omits equals, hashcode, toString, etc., showing only the constructor and add methods) :
public final class $Proxy11 extends Proxy implements Service
{ //Constructor, the argument is an instance of the MyInvocationHandler class that was just passed over.
public $Proxy11(InvocationHandler invocationhandler)
{
super(invocationhandler);
}
public final void add()
{
try
{
//It's really just calling the invoke method & NBSP; in MyInvocationHandler;
super.h.invoke(this, m3, null);
return;
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
}