詳解java中動態代理實現機制_java

來源:互聯網
上載者:User

代理模式是常用的java設計模式,它的特徵是代理類與委託類有同樣的介面,代理類主要負責為委託類預先處理訊息、過濾訊息、把訊息轉寄給委託類,以及事後處理訊息等。代理類與委託類之間通常會存在關聯關係,一個代理類的對象與一個委託類的對象關聯,代理類的對象本身並不真正實現服務,而是通過調用委託類的對象的相關方法,來提供特定的服務。

JAVA各種動態代理實現的比較

介面

interface AddInterface{ int add(int a, int b);}interface SubInterface{ int sub(int a, int b);}

實作類別

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; }}

方式1: JDK內建的動態代理

1、實現方式

  Java在JDK1.3後引入的動態代理機制,使我們可以在運行期動態建立代理類。使用動態代理實現AOP需要有四個角色:被代理的類,被代理類的介面,織入器,和InvocationHandler,而織入器使用介面反射機制產生一個代理類,然後在這個代理類中織入代碼。被代理的類是AOP裡所說的目標,InvocationHandler是切面,它包含了Advice和Pointcut。

2、vInvocationHandler介面的實現

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 + "開始, 參數: "+Arrays.asList(args));  Object result = method.invoke(real, args);  System.out.println("the method: "+methodName+"結束, 結果: " + result);  return result; }}

3、建立代理類並且調用代理類

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); }}

方式2:動態位元組碼產生(cglib)

1、實現方式

  Enhancer和MethodInterceptor。Enhancer可以用來動態產生一個類,這個類可以繼承指定的一個類,實現指定的一些介面。同時,Enhancer在產生一個類之前需要指定一個Callback,當類方法調用時,方法的執行被分配給這個Callback,MethodInterceptor是一個使用比較多的繼承自Callback的介面,它只有一個方法聲明。


2、介面InvocationHandler(jdk中)和介面MethodInterceptor(cglib中)對比

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; } 

    從參數構成上,methodInterceptor的輸入參數比Invocationhandler多1個,其實前3個參數對象的含義與Invocationhandler的含義是相同的。
  第一個參數表示調用方法來自哪個對象;
  第二個參數表示調用方法的Method對象;
  第三個參數表示此次調用的輸入參數列表;
  多出來的參數是MethodProxy 類型的,它應該是cglib產生用來代替Method對象的一個對象,使用MethodProxy比調用JDK自身的Method直接執行方法效率會有提升。
3、實現1

MethodInterceptor介面的實現

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 + "開始, 參數: "+Arrays.asList(args));  //Object result = method.invoke(real, args);//兩種方式都是可以得  Object result = proxy.invoke(real, args);  System.out.println("the method: "+methodName+"結束, 結果: " + result);  return result; }}

建立代理類並調用代理類

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); }}

注意了,MethodProxy在對執行函數的時候,提供了2個方法

public Object invoke (Object obj, Object[] args) throws Throwable public Object invokeSuper(Object obj, Object[] args) throws Throwable

  其中,javadoc上說這個invoke()方法可以用於相同類中的其他對象的方法執行,也就是說這個方法中的obj需要傳入相同一個類的另一個對象(上述方法中就是傳入了Arithmetic類的不同對象),否則會進入無限遞迴迴圈(測試之後還真是出現了StackOverflowError)。仔細的想一想就會發現,public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy)中的target是實現的代理類對象,通過target調用add()方法時會觸發intercept()方法被調用,如果在intercept()方法中再調用method.invoke(target, args),就相當於add()方法中又調用add()方法,導致無限的遞迴迴圈。但是如果執行method.invoke(real, args)則不會,因為real和target是同一個類不同對象,real是真實邏輯主題,target是真實主題real的代理。

  下面一個例子來類比一下:

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());//正確    //target.setObject(target);//發生迴圈調用    target.solve();  }}

其實Method的invoke()方法會根據obj的類型去調用對應的solve()方法,也就是多態性。

4、實現2

  MethodInterceptor介面的實現

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 + "開始, 參數: "+Arrays.asList(args));    // 列印類資訊 :target.getClass();省略    Object result = proxy.invokeSuper(target, args);    System.out.println("the method: "+methodName+"結束, 結果: " + result);    return result;  }}

建立代理類並調用代理類

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);  }}

  注意了,實現2中Enhancer 沒有設定介面,因為設定了Superclass了(也就是代理類的父類是Arithmetic),我們的代理類會繼承它,而Arithmetic已經實現了我們的介面。為了證明這一點,可以在MethodInterceptor的 intercept方法中列印 target.getClass()的類資訊,你會發現cglib的兩種方式代理類的父類是不同的。如下:

  實現1:

public class com.test.Arithmetic$$EnhancerByCGLIB$$4fa786eb extends java.lang.Object

  實現2:

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

方式3:javassist產生動態代理(代理工廠建立 或者 動態代碼建立)  

  Javassist是一個編輯位元組碼的架構,可以讓你很簡單地操作位元組碼。它可以在運行期定義或修改Class。使用Javassist實現AOP的原理是在位元組碼載入前直接修改需要切入的方法。這比使用Cglib實現AOP更加高效,並且沒太多限制,實現原理如下圖:

實現1:

介面的實現

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 + "開始, 參數: "+Arrays.asList(args));    Object result = proxy.invoke(target, args);    System.out.println("the method: "+methodName+"結束, 結果: " + result);    return result;  }}

建立代理類並調用代理類

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);  }}

注意:MethodHandler介面中invoke方法的定義,如下:

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

method代表調用方法的Method對象,proxy是代理類產生並代替method的對象,否則用method.invoke(target, args)會產生無限迴圈調用。

實現2:

  javassist使用動態java代碼常見代理過程和前文的方法略有不同。javassist內部可以通過動態java代碼,產生位元組碼。這種方式建立的動態代理可以非常靈活,甚至可以在運行時產生商務邏輯。

//自訂攔截器介面interface InterceptorHandler {     /**    * 調用動態代理對象的方法將反射本方法,可在本方法實現中添加類似AOP的事前事後操作,只有在本方法體中加入如下代碼    * 被代理的方法才會被執行,傳回值將返回給代理最後返回給程式    * @param obj Object 被代理的對象    * @param method Method 被代理對象的方法    * @param args Object[] 被代理對象的方法的參數    * @return Object 被代理對象的方法執行後的傳回值    * @throws Throwable    */   public Object invoke(Object obj, Method method, Object[] args) throws Throwable; } //攔截器的實現class InterceptorHandlerImpl implements InterceptorHandler{  @Override  public Object invoke(Object obj, Method method, Object[] args) throws Throwable {    String methodName = method.getName();    System.out.println(method);    System.out.println("the method: " + methodName + "開始, 參數: "+Arrays.asList(args));    Object result = method.invoke(obj, args);    System.out.println("the method: "+methodName+"結束, 結果: " + result);    return result;  }}class MyProxyImpl {   /** 動態代理類的類名尾碼 */   private final static String PROXY_CLASS_NAME_SUFFIX = "$MyProxy_";   /** 攔截器介面 */   private final static String INTERCEPTOR_HANDLER_INTERFACE = "com.test.InterceptorHandler";   /** 動態代理類的類名索引,防止類名重複 */   private static int proxyClassIndex = 1;      /**    * 暴露給使用者的動態代理介面,返回某個介面的動態代理對象,注意本代理實現需和com.cuishen.myAop.InterceptorHandler攔截器配合    * 使用,即使用者要使用本動態代理,需先實現com.cuishen.myAop.InterceptorHandler攔截器介面    * @param interfaceClassName String 要動態代理的介面類名, e.g test.StudentInfoService    * @param classToProxy String 要動態代理的介面的實作類別的類名, e.g test.StudentInfoServiceImpl    * @param interceptorHandlerImplClassName String 使用者提供的攔截器介面的實作類別的類名    * @return Object 返回某個介面的動態代理對象    * @throws InstantiationException    * @throws IllegalAccessException    * @throws NotFoundException    * @throws 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);   }      /**    * 動態實現要代理的介面    * @param classToProxy String 要動態代理的介面的實作類別的類名, e.g test.StudentInfoServiceImpl    * @param interfaceClass Class 要動態代理的介面類, e.g test.StudentInfoService    * @param interceptorHandlerImplClass Class 使用者提供的攔截器介面的實作類別    * @return Object 返回某個介面的動態代理對象    * @throws NotFoundException    * @throws CannotCompileException    * @throws InstantiationException    * @throws IllegalAccessException    */   private static Object dynamicImplementsInterface(String classToProxy, Class interfaceClass, Class interceptorHandlerImplClass) throws NotFoundException, CannotCompileException, InstantiationException, IllegalAccessException {     ClassPool cp = ClassPool.getDefault();     String interfaceName = interfaceClass.getName();     //動態指定代理類的類名     String proxyClassName = interfaceName + PROXY_CLASS_NAME_SUFFIX + proxyClassIndex++;     //要實現的介面的包名+介面名     String interfaceNamePath = interfaceName;          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();   }      /**    * 動態實現介面裡的方法    * @param classToProxy String 要動態代理的介面的實作類別的類名, e.g test.StudentInfoServiceImpl    * @param implementer CtClass 動態代理類的封裝    * @param methodToImpl Method 動態代理類裡面要實現的介面方法的封裝    * @param interceptorClass Class 使用者提供的攔截器實作類別    * @param methodIndex int 要實現的方法的索引    * @throws CannotCompileException    */   private static void dynamicImplementsMethodsFromInterface(String classToProxy, CtClass implementer, Method methodToImpl, Class interceptorClass, int methodIndex) throws CannotCompileException {     String methodCode = generateMethodCode(classToProxy, methodToImpl, interceptorClass, methodIndex);     CtMethod cm = CtNewMethod.make(methodCode, implementer);     implementer.addMethod(cm);   }      /**    * 動態組裝方法體,當然代理裡面的方法實現並不是簡單的方法拷貝,而是反射調用了攔截器裡的invoke方法,並將接收到的參數進行傳遞    * @param classToProxy String 要動態代理的介面的實作類別的類名, e.g test.StudentInfoServiceImpl    * @param methodToImpl Method 動態代理類裡面要實現的介面方法的封裝    * @param interceptorClass Class 使用者提供的攔截器實作類別    * @param methodIndex int 要實現的方法的索引    * @return String 動態組裝的方法的字串    */   private static String generateMethodCode(String classToProxy, Method methodToImpl, Class interceptorClass, int methodIndex) {     String methodName = methodToImpl.getName();     String methodReturnType = methodToImpl.getReturnType().getName();     Class[] parameters = methodToImpl.getParameterTypes();     Class[] exceptionTypes = methodToImpl.getExceptionTypes();     StringBuffer exceptionBuffer = new StringBuffer();     //組裝方法的Exception聲明     if(exceptionTypes.length > 0) exceptionBuffer.append(" throws ");     for(int i = 0; i < exceptionTypes.length; i++) {       if(i != exceptionTypes.length - 1) exceptionBuffer.append(exceptionTypes[i].getName()).append(",");       else exceptionBuffer.append(exceptionTypes[i].getName());     }     StringBuffer parameterBuffer = new StringBuffer();     //組裝方法的參數列表     for(int i = 0; i < parameters.length; i++) {       Class parameter = parameters[i];       String parameterType = parameter.getName();       //動態指定方法參數的變數名       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();     //方法聲明,由於是實現介面的方法,所以是public     methodDeclare.append("public ").append(methodReturnType).append(" ").append(methodName).append("(").append(parameterBuffer).append(")").append(exceptionBuffer).append(" {\n");     String interceptorImplName = interceptorClass.getName();     //方法體     methodDeclare.append(INTERCEPTOR_HANDLER_INTERFACE).append(" interceptor = new ").append(interceptorImplName).append("();\n");     //反射調用使用者的攔截器介面     methodDeclare.append("Object returnObj = interceptor.invoke(Class.forName(\"" + classToProxy + "\").newInstance(), Class.forName(\"" + classToProxy + "\").getMethods()[" + methodIndex + "], ");     //傳遞方法裡的參數     if(parameters.length > 0) methodDeclare.append("new Object[]{");      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");     //對調用攔截器的傳回值進行封裝     if(methodToImpl.getReturnType().isPrimitive()) {       if(methodToImpl.getReturnType().equals(Boolean.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("return ((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{       //分別對應 代理類要實現的介面類名, 需要代理類的類名, 使用者自訂攔截器實作類別的類名    Object proxyArithmetic = MyProxyImpl.newProxyInstance("com.test.ArithmeticInterface", "com.test.Arithmetic",                                     "com.test.InterceptorHandlerImpl");    ((ArithmeticInterface)proxyArithmetic).add(a, b);    ((ArithmeticInterface)proxyArithmetic).sub(a, b);      }}

列印一下動態實現介面的代碼如下:

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();}

以上就是關於java中動態代理實現機制的詳細介紹,希望對大家的學習有所協助。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

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.