Detailed Java dynamic Proxy implementation mechanism _java

Source: Internet
Author: User
Tags aop arithmetic reflection throwable wrapper stringbuffer

Proxy mode is a common Java design pattern, it is characterized by proxy class and delegate class have the same interface, Agent class is mainly responsible for the delegation class preprocessing messages, filtering messages, forwarding messages to the delegate class, and afterwards processing messages. There is usually an association between a proxy class and a delegate class, the object of a proxy class is associated with an object of a delegate class, and the object of the proxy class does not actually implement the service, but rather provides a specific service by invoking the methods associated with the object of the delegate class.

Comparison of various dynamic proxy implementations in Java

Interface

interface addinterface{
 int Add (int a, int b);
}

Interface subinterface{
 int sub (int a, int b);
}

Implementation class

Class arithmetic implements AddInterface, subinterface{
 @Override public
 int sub (int a, int b) {return a
  b;
 }

 @Override public
 int Add (int a, int b) {return
  a+b;
 }
}

Mode 1: Dynamic Proxy with JDK

1. Implementation mode

The dynamic proxy mechanism introduced by Java after JDK1.3 enables us to create proxy classes dynamically during runtime. Using dynamic proxies to implement AOP requires four roles: the proxy class, the interface of the proxy class, the Loom, and the Invocationhandler, and the loom uses the interface reflection mechanism to generate a proxy class and then weave the code into the proxy class. The proxy class is the target of AOP, Invocationhandler is the slice, it contains the advice and pointcut.

2, the implementation of Vinvocationhandler interface

Class Jdkdpqueryhandler implements invocationhandler{
 private arithmetic real;
 Public Jdkdpqueryhandler (arithmetic real) {
  this.real = real;
 }
 @Override Public
 object Invoke (Object proxy, Method method, object[] args) throws Throwable {
  String methodname = Method.getname ();
  System.out.println (method);
  System.out.println ("The method:" + methodname + "Start, Parameters:" +arrays.aslist (args));
  Object result = Method.invoke (real, args);
  System.out.println ("The Method:" +methodname+ "End, Results:" + result);
  return result;
 }

3. Create proxy class and Invoke proxy class

public class main{
 private static int a = 4, b = 2;
 
 public static object Createjdkproxy (arithmetic real) {
  Object proxyarithmetic = proxy.newproxyinstance ( Real.getclass (). getClassLoader (),
    Real.getclass (), Getinterfaces (), New Jdkdpqueryhandler (real); 
  return proxyarithmetic;
 }
 
 public static void Main (string[] args) {
  arithmetic real = new Arithmetic ();
  Object proxyarithmetic = Createjdkproxy (real);
  ((AddInterface) proxyarithmetic). Add (A, b);
  ((subinterface) proxyarithmetic). Sub (A, b);
 }
}

Mode 2: Dynamic byte code generation (Cglib)

1. Implementation mode

Enhancer and Methodinterceptor. Enhancer can be used to dynamically generate a class that can inherit a specified class and implement some of the specified interfaces. At the same time, enhancer needs to specify a callback before generating a class, and when the class method is called, the execution of the method is assigned to the Callback,methodinterceptor is a more inherited from the callback interface, It has only one method declaration.


2, Interface Invocationhandler (JDK) and interface Methodinterceptor (CGLIB) contrast

Public interface Methodinterceptor extends Callback {public 
 object intercept (object obj, Java.lang.reflect.Method Method, object[] Args,methodproxy proxy) throws Throwable; 
} 
Public interface Invocationhandler {public 
  object Invoke (Object proxy, Method method, object[] args) throws Throwable ; 
} 

From the composition of the parameters, Methodinterceptor input parameters than invocationhandler one, in fact, the first 3 parameter objects and the meaning of the invocationhandler is the same.
The first parameter represents which object the calling method comes from;
The second parameter represents the method object that invokes the methods;
The third parameter represents the input parameter list for this call;
The extra parameter is the Methodproxy type, it should be an object that Cglib generates to replace the method object, and it is more efficient to use methodproxy than to invoke the JDK itself to execute methods directly.
3, Achieve 1

Implementation of Methodinterceptor interface

Class Cglibdpqueryinterceptor implements methodinterceptor{
 private arithmetic real;
 Public cglibdpqueryinterceptor (arithmetic real) {
  this.real = real;
 }
 
 @Override Public
 Object Intercept (object target, Method method, object[] args, Methodproxy proxy) throws Throwable {
   string methodname = Method.getname ();
  System.out.println (method);
  System.out.println ("The method:" + methodname + "Start, Parameters:" +arrays.aslist (args));
  Object result = Method.invoke (real, args);//Two ways you can get object result
  = Proxy.invoke (real, args);
  System.out.println ("The Method:" +methodname+ "End, Results:" + result);
  return result;
 }

To create a proxy class and invoke the proxy class

public class main{
 private static int a = 4, b = 2;
 public static Object Createcglibproxy (arithmetic real) {
   enhancer enhancer = new enhancer ();
   Enhancer.setcallback (new Cglibdpqueryinterceptor (real));
   Enhancer.setinterfaces (Real.getclass (). Getinterfaces ());
   return Enhancer.create ();
 }
 
 public static void Main (string[] args) {
  arithmetic real = new Arithmetic ();  
  Object proxyarithmetic = Createcglibproxy (real);  
  ((AddInterface) proxyarithmetic). Add (A, b);
  ((subinterface) proxyarithmetic). Sub (A, b);
 }
}

Note that Methodproxy provides 2 methods for executing a function

public object invoke (Object obj, object[] args) throws throwable the public 
object Invokesuper (Object obj, object[) args ) throws Throwable

In which, Javadoc says that the Invoke () method can be used to execute methods of other objects in the same class, that is, obj in this method needs to pass in another object of one of the same classes (the above method is to pass in the different objects of the arithmetic class). Otherwise, you will enter an infinite recursive loop (stackoverflowerror after the test). Think carefully and you will find that the target in the public object intercept (object target, Method method, object[] args, Methodproxy proxy) is the proxy class object that is implemented. Invoking the Add () method via target triggers The Intercept () method to be invoked, and if Method.invoke (target, args) is called again in The Intercept () method, the Add () method is called in the Add () method. leads to infinite recursive loops. However, if the execution of Method.invoke (real, args) is not, because the true and target are different objects of the same class, really is the actual logical theme, and target is the agent of real subject.

Here's an example to simulate:

Interface solveinterface{
 void Solve ();
}

Class Real implements solveinterface{public
 void Solve () {
  System.out.println (' real solve! ');
 }

Class Target extends real{
 private Object obj;
 
 public void SetObject (Object obj) {
  this.obj = obj;
 }
 
 private void Invoke () {
  try {method method
   = SolveInterface.class.getMethod ("Solve", new class[]{});
   Method.invoke (obj, new class[]{});
  catch (Exception e) {
   e.printstacktrace ();
  }
 }
 public void Solve () {
  System.out.println ("Target solve!");
  Invoke ();
 }


public class Main{public static void Main (string[] args) throws exception{   
    target target = new target ();
    Target.setobject (new Real ());//correct
    //target.setobject (target); loop call
    target.solve ();
  }
}

In fact, the Invoke () method of methods invokes the corresponding solve () method, which is polymorphism, according to the type of obj.

4, Achieve 2

Implementation of Methodinterceptor interface

Class Cglibdpqueryinterceptor implements methodinterceptor{
  
  @Override public
  object Intercept (object target, Method method, object[] args, Methodproxy proxy) throws Throwable {
    String methodname = Method.getname ();
    System.out.println (method);
    System.out.println ("The method:" + methodname + "Start, Parameters:" +arrays.aslist (args));
    Printing class Information: Target.getclass (); omitting
    Object result = Proxy.invokesuper (target, args);
    System.out.println ("The Method:" +methodname+ "End, Results:" + result);
    return result;
  }

To create a proxy class and invoke the proxy class

public class main{
  private static int a = 4, b = 2;
public static Object Createcglibproxy () {
     enhancer enhancer = new enhancer ();
     Enhancer.setcallback (New Cglibdpqueryinterceptor ());
     Enhancer.setsuperclass (arithmetic.class);
     return Enhancer.create ();
  }
  
  public static void Main (string[] args) {
    arithmetic real = new Arithmetic ();

    Object proxyarithmetic = Createcglibproxy ();
    
    ((AddInterface) proxyarithmetic). Add (A, b);
    ((subinterface) proxyarithmetic). Sub (A, b);
  }
}

Note that implementation 2 Enhancer does not have an interface set, because the superclass is set (that is, the parent class of the proxy class is arithmetic), and our proxy class inherits it, and arithmetic has implemented our interface. To prove this, you can print the class information of Target.getclass () in the Methodinterceptor intercept method, and you will find that the parent class of the proxy class is different for the Cglib two ways. As follows:

Implementation 1:

public class com.test.arithmetic$ $EnhancerByCGLIB $$4fa786eb extends Java.lang.Object

Implementation 2:

public class com.test.arithmetic$ $EnhancerByCGLIB $$4fa786eb extends Com.test.Arithmetic

Mode 3: javassist Generate dynamic proxies (Agent factory creation or dynamic code creation)

Javassist is a framework for editing bytecode that allows you to manipulate bytecode very simply. It can define or modify class at run time. The principle of using javassist to implement AOP is to modify the method that needs to be cut directly before bytecode loading. This is more efficient than using cglib to implement AOP, and there are not too many limitations, the principle of implementation is as follows:

Implementation 1:

Implementation of the interface

Class Javassistdpqueryhandler implements methodhandler{

  @Override public
  object Invoke (object target, method method, method Proxy, object[] args throws Throwable {
    String methodname = Method.getname ();
    System.out.println (method);
    System.out.println ("The method:" + methodname + "Start, Parameters:" +arrays.aslist (args));
    Object result = Proxy.invoke (target, args);
    System.out.println ("The Method:" +methodname+ "End, Results:" + result);
    return result;
  }

To create a proxy class and invoke the proxy class

public class main{
  private static int a = 4, b = 2;
public static Object Createjavassistproxy () throws exception{
    proxyfactory factory = new Proxyfactory ();
    Factory.setsuperclass (arithmetic.class);
    Factory.sethandler (New Javassistdpqueryhandler ());
    Return Factory.createclass (). newinstance ();
  
  public static void Main (string[] args) throws exception{
    arithmetic real = new Arithmetic ();
    
    Object proxyarithmetic = Createjavassistproxy ();
    
    ((AddInterface) proxyarithmetic). Add (A, b);
    ((subinterface) proxyarithmetic). Sub (A, b);
  }
}

Note: The definition of the Invoke method in the Methodhandler interface is as follows:

public object Invoke (Object target, Method method, method proxy, object[] args)

Method represents an object that invokes methods, which are generated by the proxy class and that are substituted for the means, otherwise, method.invoke (target, args) produces an infinite loop call.

Implementation 2:

Javassist The common agent process using dynamic Java code is slightly different from the method used in the preceding article. Javassist can generate bytecode internally through dynamic Java code. Dynamic proxies created in this way can be very flexible and can even generate business logic at run time.

Custom Interceptor Interface Interface Interceptorhandler {/** * The method that invokes the dynamic proxy object will reflect this method, and you can add the ex post operation similar to AOP in the implementation of this method, and only add the following code in this method body The proxy's method is executed, and the return value is returned to the agent's final return to the program * @param obj Object-Proxy Object * @param method methods to be proxy objects * @param args object[]  The parameter of the method of the proxy object * @return The return value after the method of the object is executed * @throws Throwable */Public object invoke (Object obj. 
method, object[] args) throws Throwable; }//Interceptor implementation class Interceptorhandlerimpl implements interceptorhandler{@Override public object Invoke (object obj, Me
    Thod method, object[] args) throws Throwable {String methodname = Method.getname ();
    System.out.println (method);
    System.out.println ("The method:" + methodname + "Start, Parameters:" +arrays.aslist (args));
    Object result = Method.invoke (obj, args);
    System.out.println ("The Method:" +methodname+ "End, Results:" + result);
  return result; 
  The class Myproxyimpl {/** dynamic proxy class name suffix */private final static String Proxy_class_name_suffix = "$MyProxy _"; /**Interceptor Interface */private final static String Interceptor_handler_interface = "Com.test.InterceptorHandler"; 
   
  /** the class name index of the dynamic proxy class to prevent class name repetition * * private static int proxyclassindex = 1; /** * Exposed to the user's dynamic proxy interface, return an interface of the dynamic proxy object, note that this proxy implementation needs and Com.cuishen.myAop.InterceptorHandler interceptor with the * use, that is, users want to use this dynamic agent, you need to realize the first com.cuish En.myAop.InterceptorHandler Interceptor Interface * @param interfaceclassname String the interface class name to be dynamically proxied, e.g test. Studentinfoservice * @param classtoproxy String The class name of the implementation class for the interface to be dynamically proxied, e.g test. Studentinfoserviceimpl * @param interceptorhandlerimplclassname String User-supplied Interceptor interface class name of the implementation class * @return Object returns the dynamic of an interface State proxy Object * @throws instantiationexception * @throws illegalaccessexception * @throws notfoundexception * @thr 
  oWS cannotcompileexception * @throws classnotfoundexception * @see Com.cuishen.myAop.InterceptorHandler * * public static Object newproxyinstance (String interfaceclassname, String classtoproxy, String Interceptorhandlerimplclassname) throws Instantiationexception, IllEgalaccessexception, Notfoundexception, Cannotcompileexception, classnotfoundexception {class InterfaceClass = Class 
    . forname (Interfaceclassname); 
    Class Interceptorhandlerimplclass = Class.forName (interceptorhandlerimplclassname); 
  Return Dynamicimplementsinterface (Classtoproxy, Interfaceclass, Interceptorhandlerimplclass); /** * Dynamically implements the interface to proxy * @param classtoproxy String The class name of the implementation class of the interface to be dynamically proxied, e.g test. Studentinfoserviceimpl * @param Interfaceclass class to dynamically proxy interface classes, e.g test. Studentinfoservice * @param interceptorhandlerimplclass class User-provided implementation classes for interceptor interfaces * @return object returns a dynamic proxy object for an interface * @throws notfoundexception * @throws cannotcompileexception * @throws instantiationexception * @throws Illegala  Ccessexception */private static Object Dynamicimplementsinterface (String Classtoproxy, class Interfaceclass, class Interceptorhandlerimplclass) throws Notfoundexception, Cannotcompileexception, Instantiationexception, Illegalaccessexception {classpool cp = Classpool.getdefault (); 
    String InterfaceName = Interfaceclass.getname (); 
    dynamically specifies the class name of the proxy class String proxyclassname = InterfaceName + Proxy_class_name_suffix + proxyclassindex++; 
     
    The package name + interface name String Interfacenamepath = InterfaceName of the interface to be implemented; 
    Ctclass ctinterface = Cp.getctclass (Interfacenamepath); 
    Ctclass cc = Cp.makeclass (proxyclassname); 
    Cc.addinterface (Ctinterface); 
    method [] Methods = Interfaceclass.getmethods (); 
      for (int i = 0; i < Methods.length i++) {Method method = Methods[i]; 
    Dynamicimplementsmethodsfrominterface (Classtoproxy, Cc, method, Interceptorhandlerimplclass, i); 
  Return (Object) Cc.toclass (). newinstance (); /** * Dynamic implementation of the method in the interface * @param classtoproxy String The class name of the implementation class of the interface to be dynamically proxied, e.g test.  Studentinfoserviceimpl * @param implementer ctclass Dynamic proxy class Packaging * @param Methodtoimpl method Dynamic proxy class The wrapper for the interface methods to implement * @param interceptorclass class User-provided interceptor implementation classes * @param methOdindex int the method to implement the index * @throws cannotcompileexception * * private static void Dynamicimplementsmethodsfrominter Face (String classtoproxy, Ctclass Implementer, Method Methodtoimpl, Class interceptorclass, int methodindex) throws Canno tcompileexception {String Methodcode = Generatemethodcode (Classtoproxy, Methodtoimpl, Interceptorclass, MethodIndex) 
    ; 
    Ctmethod cm = Ctnewmethod.make (methodcode, implementer); 
  Implementer.addmethod (CM); /** * Dynamic assembly method body, of course, the implementation of the method inside the agent is not a simple method copy, but the reflection calls the interceptor invoke method, and will receive the parameters passed * @param classtoproxy String to dynamically proxy The class name of the implementation class for the interface, e.g test.  
   Studentinfoserviceimpl * @param Methodtoimpl method The package of interface methods to implement in Dynamic proxy class * @param the Interceptor implementation class provided by the Interceptorclass class user * @param methodindex int An index of the method to be implemented * @return string of the method that is dynamically assembled by String */private static string Generatemethodcod E (String classtoproxy, Method Methodtoimpl, Class interceptorclass, int methodindex) {String methodname = Methodtoim 
    Pl.getname (); String MeThodreturntype = Methodtoimpl.getreturntype (). GetName (); 
    class[] Parameters = Methodtoimpl.getparametertypes (); 
    class[] Exceptiontypes = Methodtoimpl.getexceptiontypes (); 
    StringBuffer exceptionbuffer = new StringBuffer (); 
    Exception Declaration of assembly method if (Exceptiontypes.length > 0) exceptionbuffer.append ("throws"); for (int i = 0; i < exceptiontypes.length i++) {if (I!= exceptiontypes.length-1) exceptionbuffer.append (excep 
      Tiontypes[i].getname ()). Append (","); 
    Else Exceptionbuffer.append (Exceptiontypes[i].getname ()); 
    } stringbuffer Parameterbuffer = new StringBuffer (); 
      The parameter list for the assembly method for (int i = 0; i < parameters.length i++) {Class parameter = Parameters[i]; 
      String parametertype = Parameter.getname (); 
      Variable name for dynamically specifying method parameters String RefName = "a" + I; 
      if (i!= parameters.length-1) parameterbuffer.append (parametertype). Append ("" + RefName). Append (","); else Parameterbuffer.append (parametertyPE). Append ("" + refName); 
    } stringbuffer methoddeclare = new StringBuffer (); The method declaration, because it is the method that implements the interface, is public methoddeclare.append ("public"). Append (Methodreturntype). Append (""). Append ( 
    methodname). Append ("("). Append (Parameterbuffer). Append (")"). Append (Exceptionbuffer). Append ("{\ n"); 
    String interceptorimplname = Interceptorclass.getname (); Method Body Methoddeclare.append (Interceptor_handler_interface). Append ("interceptor = new"). Append (Interceptorimplname) 
    . Append ("(); \ n"); Reflection invokes the user's Interceptor Interface methoddeclare.append ("Object returnobj = Interceptor.invoke (Class.forName (\" + classtoproxy + "\"). NE 
    Winstance (), Class.forName (\ "" + Classtoproxy + "\"). GetMethods () ["+ Methodindex +"], ");  
    The parameter if (Parameters.length > 0) methoddeclare.append ("New object[]{") in the transfer method; for (int i = 0; i < parameters.length i++) {//($W) converts from a primitive type to the corresponding wrapper 
      type:e.g. 
      Integer i = ($w) 5; if (i!= parameters.length-1) Methoddeclare.append ("($w) a" + i + ","); 
    Else Methoddeclare.append ("($w) a" + i); 
    if (Parameters.length > 0) methoddeclare.append ("}); \ n"); 
    else Methoddeclare.append ("null); \ n"); Wraps the return value of the calling interceptor if (Methodtoimpl.getreturntype (). Isprimitive ()) {if (Methodtoimpl.getreturntype (). Equals (Boole An. 
      TYPE)) Methoddeclare.append ("Return ((Boolean) returnobj). Booleanvalue (); \ n"); else if (Methodtoimpl.getreturntype (). Equals (Integer.type)) Methoddeclare.append ("Return" ((Integer) returnobj). 
      Intvalue (); \ n "); else if (Methodtoimpl.getreturntype (). Equals (Long.type)) Methoddeclare.append ("Return" ((Long) returnobj). Longvalue ( 
      ); \ n "); else if (Methodtoimpl.getreturntype (). Equals (Float.type)) Methoddeclare.append ("Return" ((Float) returnobj). 
      Floatvalue (); \ n "); else if (Methodtoimpl.getreturntype (). Equals (Double.type)) Methoddeclare.append ("Return (Double) returnobj). 
      Doublevalue (); \ n "); else if (Methodtoimpl.getreturntype (). Equals (Character.type)) Methoddeclare.append ("Return (Character) returnobj). Charvalue (); \ n"); else if (Methodtoimpl.getreturntype (). Equals (Byte.type)) Methoddeclare.append ("Return" ((Byte) returnobj). Bytevalue ( 
      ); \ n "); else if (Methodtoimpl.getreturntype (). Equals (Short.type)) Methoddeclare.append ((short) returnobj). 
    Shortvalue (); \ n "); 
    else {methoddeclare.append ("return (" + Methodreturntype + ") returnobj;\n"); 
    } methoddeclare.append ("}"); 
    System.out.println (Methoddeclare.tostring ()); 
  return methoddeclare.tostring (); } public class main{public static void Main (string[] args) throws exception{//corresponding to the interface class name to be implemented by the proxy class, it is necessary to substitute Class name of the class, user-defined Interceptor implementation class class name Object proxyarithmetic = myproxyimpl.newproxyinstance ("Com.test.ArithmeticInterface", "
    Com.test.Arithmetic "," Com.test.InterceptorHandlerImpl ");
    ((Arithmeticinterface) proxyarithmetic). Add (A, b); ((Arithmeticinterface) ProxyarithmeTIC). Sub (A, b);
 }
}

Print the code for the dynamic implementation interface as follows:

public int Add (int a0,int A1) {
Com.test.InterceptorHandler interceptor = new Com.test.InterceptorHandlerImpl ();
Object returnobj = Interceptor.invoke (Class.forName ("com.test.Arithmetic"). Newinstance (), Class.forName (" Com.test.Arithmetic "). GetMethods () [0], New object[]{($w) A0, ($w) A1});
Return ((Integer) returnobj). Intvalue ();
public int sub (int a0,int A1) {
Com.test.InterceptorHandler interceptor = new Com.test.InterceptorHandlerImpl ();
Object returnobj = Interceptor.invoke (Class.forName ("com.test.Arithmetic"). Newinstance (), Class.forName (" Com.test.Arithmetic "). GetMethods () [1], New object[]{($w) A0, ($w) A1});
Return ((Integer) returnobj). Intvalue ();

The above is about the dynamic agent implementation mechanism in Java in detail, I hope to help you learn.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.