動態代理模式

最近在看項目中關於aop的一些東西,正好想起之前沒畢業的時候去唯品會面試,面試官問我知不知道aop底層實現原理,當時真是一臉懵逼啊。現在工作後對aop怎麼使用稍微瞭解了一點,也知道底層原理其實是動態代理,具體怎麼實現還不是很清楚所以這篇就聊一聊動態代理設計模式。在這裏分享一下之前無意中看到的大神對aop的比喻,感覺很有道理。

aop切面編程就是在常規的執行java類中方法前或執行後加入自定義的方法。比如你本來每天都去打醬油,去,打醬油,回。現在我每天在你打醬油路上等着,你去打醬油的時候我打你一頓,回來的時候給你點糖果吃。你根本不知道爲什麼我會在路上攔住打你。所以在切面中插入你自定義的方法,這個方法的執行和本身要執行的類方法無關係,也就是不是這個類的方法來調用你寫的方法的,你寫的方法什麼時候執行都是要通過在配置指定。我打完你,你該打醬油還是去打醬油,當然我如果是攔住你讓你醬油打少點,你打醬油的時候還是會打那麼多,但是在你打完醬油回來的時候我可以把你的醬油倒些出去,所以嵌入的自定義方法對要調用的類方法本身沒有影響,但是可以操縱這個方法的返結果或者處理結果。

上面這段話我覺得還是很形象的,我現在接觸到的aop的應用一般是日誌以及異常處理,session之類的還不是很瞭解。

按照我現在的理解,我覺得動態代理只要有兩個地方比較重要,一個是InvocationHandler接口,還有一個是Proxy類。

爲什麼要有代理模式?

在某些情況下,一個客戶不想或者不能直接引用另一個對象,而代理對象可以在客戶端和目標對象之間起到中介的作用。比如我們買二手房,往往只能通過中介,因爲我們這時候不能直接接觸到客戶,但是中介可以。

代理模式代碼如下:

首先建立buyHouse接口

public interface BuyHouse {

	public void buyHouse();
}
建立一個realSubject

public class RealSubject implements BuyHouse {

	@Override
	public void buyHouse() {
		System.out.println("我要在外灘旁邊買一套房");
	}

}
定義一個動態代理類,實現InvocationHandler接口

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

public class DynamicProxy implements InvocationHandler {

	private Object object;
	
	public DynamicProxy(Object object){
		this.object = object;
	}
	@Override
	public Object invoke(Object paramObject, Method paramMethod, Object[] paramArrayOfObject) throws Throwable {
		//實現真實對象前可以添加自己想實現的業務,實際可能是日誌,權限驗證等等
		System.out.println("你必須給我中介費先付5w");
		//實現真實對象方法
		paramMethod.invoke(object, paramArrayOfObject);
		
		//之後繼續實現一些業務,可以通過回調
		System.out.println("中介費尾款給我");
		return null;
	}

}
最後看一下測試類

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

public class TestDynamicProxy {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		BuyHouse buyHouse = new RealSubject();
		
		InvocationHandler handler = new DynamicProxy(buyHouse);
		
		BuyHouse proxyHouse = (BuyHouse) Proxy.newProxyInstance(handler.getClass().getClassLoader(), buyHouse.getClass().getInterfaces(), handler);
		
		proxyHouse.buyHouse();
	}

}
控制檯輸出結果爲

你必須給我中介費先付5w
我要在外灘旁邊買一套房
中介費尾款給我


到此建議動態代理結束,我們看下jdk1.8中InvocationHandler以及Proxy源碼。首先是InvocationHandler

public abstract interface InvocationHandler
{
  public abstract Object invoke(Object paramObject, Method paramMethod, Object[] paramArrayOfObject)
    throws Throwable;
}
InvocationHandler接口中只有一個invoke方法,該方法三個參數分別指 真實對象,調用對象的method方法,最後一個不是很明白作用,待以後補充,或者知道的大神解答下

Proxy



Proxy類在jdk1.8中定義的屬性如圖,這裏我們主要使用newProxyInstance這個方法(我也不知道爲什麼,我一看到Instance這個就想到反射),代碼如下

  public static Object newProxyInstance(ClassLoader paramClassLoader, Class<?>[] paramArrayOfClass, InvocationHandler paramInvocationHandler)
    throws IllegalArgumentException
  {
    Objects.requireNonNull(paramInvocationHandler);
    Class[] arrayOfClass = (Class[])paramArrayOfClass.clone();
    SecurityManager localSecurityManager = System.getSecurityManager();
    if (localSecurityManager != null) {
      checkProxyAccess(Reflection.getCallerClass(), paramClassLoader, arrayOfClass);
    }
    Class localClass = getProxyClass0(paramClassLoader, arrayOfClass);
    try
    {
      if (localSecurityManager != null) {
        checkNewProxyPermission(Reflection.getCallerClass(), localClass);
      }
      Constructor localConstructor = localClass.getConstructor(constructorParams);
      localObject = paramInvocationHandler;
      if (!Modifier.isPublic(localClass.getModifiers())) {
        AccessController.doPrivileged(new PrivilegedAction()
        {
          public Void run()
          {
            this.val$cons.setAccessible(true);
            return null;
          }
        });
      }
      return localConstructor.newInstance(new Object[] { paramInvocationHandler });
    }
    catch (IllegalAccessException|InstantiationException localIllegalAccessException)
    {
      throw new InternalError(localIllegalAccessException.toString(), localIllegalAccessException);
    }
    catch (InvocationTargetException localInvocationTargetException)
    {
      Object localObject = localInvocationTargetException.getCause();
      if ((localObject instanceof RuntimeException)) {
        throw ((RuntimeException)localObject);
      }
      throw new InternalError(((Throwable)localObject).toString(), (Throwable)localObject);
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new InternalError(localNoSuchMethodException.toString(), localNoSuchMethodException);
    }
  }
這段代碼我只能看個大體,基本就是利用反射獲取構造函數對象並生成一個代理類,看一下他的三個參數:

ClassLoader paramClassLoader, Class<?>[] paramArrayOfClass, InvocationHandler paramInvocationHandler)
paramClassLoader指類裝載器,paramArrayOfClass 一般表示需要提供給代理對象的需要的接口,最後InvocationHandler不多做介紹了。










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