代理模式&java動態代理

       所謂代理模式是指:爲其他對象提供一種代理以控制對這個對象的訪問。可以簡單的理解爲,一個類要做某個事,這個類可以把做這個事的權限交予其他類,其他類就成爲了這個類的代理者。比如總經理可以安排自己的日程,也可以把這個任務委託給祕書來做,那麼這麼做的意義是什麼呢?就是祕書做這件事的時候可以做一些總經理不會做的準備性的工作。

       

      第一個例子是一個簡單的代理模式的實現:

      Interface:

package com.gc.impl;

public interface TimeBookInterface{
	public void doAuditing(String name);
}
       Implements:
package com.gc.action;

import com.gc.impl.TimeBookInterface;

public class TimeBook implements TimeBookInterface{	
	public void doAuditing(String name){
		System.out.println("audit now...");
	}
}
      Proxy:
package com.gc.action;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;

import com.gc.impl.TimeBookInterface;

public class TimeBookProxy{
	private Logger logger = Logger.getLogger(this.getClass().getName());
	private TimeBookInterface timeBookInterface;
	public TimeBookProxy(TimeBookInterface timeBookInterface){
		this.timeBookInterface = timeBookInterface;
	}
	public void doAuditing(String name){
		logger.log(Level.INFO, name+"開始審覈數據");
        this.timeBookInterface.doAuditing(name);
		logger.log(Level.INFO, name+"結束審覈數據");
	}
}

       Test類:

package com.gc.test;

import com.gc.action.*;

public class TestHelloWorld{
	public static void main(String[] args) {
		TimeBookProxy timeBookProxy = new TimeBookProxy(new TimeBook());
		timeBookProxy.doAuditing("張三");
	}
}

       通過將實現類傳入ProxyClass,代理類達到了代理的目的。我們看到代理類調用了指定的方法,並且在調用方法的前後增加了必要的打log邏輯。 

       這種代理模式不侷限於java,同樣可以用其他面嚮對象語言實現。

       

       java同樣提供了一種動態代理方式,這種方式基於java的反射的特性,下面提供一個動態代理的例子。接口和類是上例中的類。

       LogProxy類:

package com.gc.action;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class logProxy implements InvocationHandler{
    
	private Object delegate;
	
	public Object bind(Object delegate){
		this.delegate = delegate;
		return Proxy.newProxyInstance(delegate.getClass().getClassLoader(), delegate.getClass().getInterfaces(), this);
	}
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		System.out.println("before");
		Object result = method.invoke(delegate, args);
		System.out.println("after");
		return result;
	}
	
}

      Test類:

package com.gc.test;

import com.gc.action.*;
import com.gc.impl.*;

public class TestHelloWorld{
	public static void main(String[] args){
		logProxy logProxy1 = new logProxy();
		TimeBookInterface timeBookProxy = (TimeBookInterface)logProxy1.bind(new TimeBook());
		timeBookProxy.DoAuditing("張三");
	}
}
       下面分析一下整個代理的過程:

TimeBookInterface timeBookProxy = (TimeBookInterface)logProxy1.bind(new TimeBook());
public Object bind(Object delegate){
		this.delegate = delegate;
		return Proxy.newProxyInstance(delegate.getClass().getClassLoader(), delegate.getClass().getInterfaces(), this);
	}
	
       ProxyClass的bind操作接受一個實現類作爲自己的參數,並返回一個代理類,程序將代理類強制轉化爲TimeBookInterface類型,然後調用TimeBookInterface的相應方法。        先看下運行結果:

       

       最後調用

timeBookProxy.DoAuditing("張三");
       方法之後,程序不僅調用了實現類中的doAuditing方法,而且執行了代理類中的兩次打印操作,說明最後執行了Proxy類的invoke方法。

       那麼爲什麼通過TimeBookInterface調用doAuditing方法可以會調用invoke方法呢?筆者在剛接觸這個特性的時候有兩個想法:

       1、Proxy.newProxyInstance方法調用返回的肯定是一個代理類,並且實現了TimeBookInterface的接口。否則不能強制轉化爲TimeBookInterface類型。

       2、這個代理類還要繼承自logProxy類,否則它不能調用invoke方法。

       事實這個類是什麼樣子呢?我們在main方法最前面加上,將生成的ProxyClass打印出來

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
		
       通過JavaDecompiler這個工具得到這個類:

import com.gc.impl.TimeBookInterface;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy
  implements TimeBookInterface
{
  private static Method m1;
  private static Method m3;
  private static Method m0;
  private static Method m2;

  public $Proxy0(InvocationHandler paramInvocationHandler)
    throws 
  {
    super(paramInvocationHandler);
  }

  public final boolean equals(Object paramObject)
    throws 
  {
    try
    {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final void DoAuditing(String paramString)
    throws 
  {
    try
    {
      this.h.invoke(this, m3, new Object[] { paramString });
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final int hashCode()
    throws 
  {
    try
    {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final String toString()
    throws 
  {
    try
    {
      return (String)this.h.invoke(this, m2, null);
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  static
  {
    try
    {
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m3 = Class.forName("com.gc.impl.TimeBookInterface").getMethod("DoAuditing", new Class[] { Class.forName("java.lang.String") });
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
  }
}

        可以看到,當調用這個類的doAuditing方法的時候調用的是 this.h.invoke(this, m3, new Object[] { paramString }) 

        這個類本身繼承自Proxy類,查看Proxy源碼可知h是invocationHandler,也就是說這個類通過調用傳入的invocationHandler的invoke方法而實現代理類中相應方法的調用。在這個例子中,由於logProxy方法實現了invocationHandler,並且把這個參數傳給了生成的代理類,這個代理類調用doAuditing方法也就是調用了logProxy方法中的invoke方法,從而達到的代理的效果。

       也就是說,第二個猜想是錯誤的。生成的代理類並沒有繼承logProxy而是繼承自Proxy類,而Proxy類中包含invocationHandler屬性,在newProxyInstance的時候,invocationHandler已經傳入要生成的代理類,通過調用這個invocationHandler的invoke方法,達到調用logProxy中的invoke方法的目的。 

       

      

       

       

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章