[102]技術-設計模式[代理模式]

weixin_34249678發表於2017-12-28

代理模式

對於client -> server 訪問模式,如果我想在client 訪問 server時候多做一些工作,那麼可以是用程式碼,互動模式就變為client-> serverProxy -> server.
其具體的java 虛擬碼如下:

public interface iA
{
    public void do();
}
public class RealA implement iA
{
    public void do()
   {
     //dosomething....
   }
}

public  ProxyA implement iA  //運用代理後客戶端訪問介面不變所以需要繼承相同的介面。
{
    RealA a;
    public void do()
   {
        //create A if a is not create
       //dosomething  before .....
       a.do();
       //dosomething  after .....
   }
}

main()
{
     iA a = new ProxyA();
}

動態代理模式

現在我們知道了簡單的代理模式,如果A有多個實現類,比如RealA,RealA1,RealA2,難道我要對每個RealA? 類寫一個代理類嘛?且就算你寫了 ProxyRealA,ProxyRealB,我們也不太好統一呼叫。
代理有一個目的就是client -> proxy -> server這種模式中的server 封裝成一種統一呼叫,不用管其內部實現的複雜性。
那麼我們有沒有辦法在程式執行時候自動生成ProxyReal呢?
其大概虛擬碼如下:

InvocationHanderAimpl implements  InvacationHandler
{
    /**
    * 這個方法實現就是類似ProxyA.do()方法類似的功能,在呼叫具體方法前後做一些處理。<br>
    */
    public Object invoke(Object proxy, Method method, Object[] args)
    {
           ... do something before invoke.
           method.invoke(proxy,args);
           .. do something after invoke.
    }
} 
main()
{
     // getProxyInstance() 為iA a = new ProxyA();動態代理版本。
     // InvocationHandler 代表了該代理類具體行為,即執行方法前後如何處理。
     iA dynamicProxyObj = getProxyInstance(A.class,Aimpl.class,InvocationHandler);
     dynamicProxyObj .do();
}

動態代理的兩種具體實現

  • jdk 的版本
public class ProxyHandler implements InvocationHandler   
{   
  private Object proxied;   
     
  public ProxyHandler( Object proxied )   
  {   
    this.proxied = proxied;   
  }   
     
  public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable   
  {   
    //在轉調具體目標物件之前,可以執行一些功能處理
    //轉調具體目標物件的方法
    return method.invoke( proxied, args);  
    //在轉調具體目標物件之後,可以執行一些功能處理
  }    
} 

main()
{
    RealA  realA= new RealA  ();   
    iA  proxiedA= (A)Proxy.newProxyInstance(A.class.getClassLoader(), 
     new Class[]{iA.class}, 
     new ProxyHandler(realA));
     iA.do();
}

這裡重點注意:
1.invocationHandler的實現。
2.newProxyInstance 的方法引數,interfaces只接收介面型別,即iA.class ,不接收RealA.class

//proxy.java
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h){
}
  • cglib版實現
    關於cglib,從網上找到英文版的介紹:

Cglib is an open source library that capable creating and loading class files in memory during Java run time. To do that it uses Java byte-code generation library ‘asm’, which is a very low level byte code creation tool,其開源類庫為:net.sf.cglib.proxy.*。

我們先看看原始碼如何建立Cglib代理類的

package com.thoughtworks.proxy.factory;

.......
import net.sf.cglib.core.CodeGenerationException;
import net.sf.cglib.core.DefaultNamingPolicy;
import net.sf.cglib.core.Predicate;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.Factory;
import net.sf.cglib.proxy.InvocationHandler;
import net.sf.cglib.proxy.Proxy;
.......
public class CglibProxyFactory extends AbstractProxyFactory {
{
public <T> T createProxy(Invoker invoker, Class... types) {
        Class type = this.getSingleClass(types); //type =null ,說明types物件全部是介面,沒有一個類
        if(type == null) {
            //全部是介面使用標準jdk的代理方式建立代理類,即proxy.newProxyInstance();
            return standardProxyFactory.createProxy(invoker, types);
        } else if(type.isPrimitive()) {
            throw new IllegalArgumentException("Cannot subclass primitive type");
        } else {
            Class[] interfaces = this.getInterfaces(types);
            Enhancer enhancer = new Enhancer();

            while(true) {
                enhancer.setSuperclass(type);
                enhancer.setInterfaces(interfaces);
                enhancer.setCallback(new CglibProxyFactory.CGLIBInvocationHandlerAdapter(invoker));

                Object proxy;
                try {
                    proxy = enhancer.create();
                    return proxy;
                } catch (CodeGenerationException var8) {
                    Throwable wrapper = var8.getCause();
                    if(wrapper != null && wrapper.getCause() instanceof SecurityException && enhancer.getNamingPolicy() != this.namingPolicy) {
                        enhancer.setNamingPolicy(this.namingPolicy);
                        continue;
                    }
                } catch (IllegalArgumentException var9) {
                    ;
                } catch (NoSuchMethodError var10) {
                    ;
                }

                proxy = this.createWithConstructor(type, enhancer);
                return proxy;
            }
        }
    }
}

其具體如何建立和使用CglibProxy可以參考部落格:http://blog.csdn.net/ljphhj/article/details/20483171

兩種方式的優缺點:

CGLib建立的動態代理物件效能比JDK建立的動態代理物件的效能高不少,但是CGLib在建立代理物件時所花費的時間卻比JDK多得多,所以對於單例的物件,因為無需頻繁建立物件,用CGLib合適,反之,使用JDK方式要更為合適一些。同時,由於CGLib由於是採用動態建立子類的方法,對於final方法,無法進行代理!

說在後面的話

這篇文章只是簡單的介紹proxy幹了一件什麼事情。
從具體的技術層面還有以下疑問:
1.ClassLoader 裝載的是哪個類?如果是一個介面的話類是如何載入進來的。
2.Cglib與jdk Proxy類的區別。
這兩個問題以後待分析。

參考

https://stackoverflow.com/questions/10664182/what-is-the-difference-between-jdk-dynamic-proxy-and-cglib

關於cglib:
https://www.programcreek.com/java-api-examples/index.php?api=net.sf.cglib.proxy.MethodInterceptor
https://github.com/cglib/cglib/wiki/Tutorial

相關文章