代理模式

1、靜態代理

   靜態代理仍是相對簡單的,就是若是我是代理,我看中你的功能。我就和你聯繫(關聯),而後我在作一些本身的工做,好比宣傳推廣,順便賺取一些外快之類的事情。java

   這中間最重要的兩件事情一是:聯繫(關聯),我要代理你的產品(功能),首先我要聯繫你。二是:咱們對外作一樣的事情(實現一樣的接口),好比我代理12306賣車票,那麼咱們都提供了賣車票這個服務(實現這個接口)。這一點很容易就變成了裝飾模式或者簡單的依賴關係。spring

下面來一個例子,如今有一個賣藥的接口以下:編程

public interface SaleMedicine {
   
    public void saleMedicine();
 
}

有一個藥廠類實現了這個接口,他在賣藥以前老是要生產藥:緩存

public class MedicineFactory implements SaleMedicine{
 
    @Override
    public void saleMedicine() {
       
        produceMedicine();
        System.out.println("賣藥");
    }
   
    private void produceMedicine()
    {
        System.out.println("生產藥品");
    }
 
}

   還有一個醫院類,也實現了賣藥接口,可是它不能生產藥啊,因此它以後作代理,一是它聯繫一個藥廠作藥廠的代理(關聯一個藥廠類):jvm

public class Hospital implements SaleMedicine {
   
    private SaleMedicine saleMedicine;
   
    @Override
    public void saleMedicine() {
        System.out.println("醫生看病");
        saleMedicine.saleMedicine();
        System.out.println("收錢");
 
    }
 
    public SaleMedicine getSaleMedicine() {
        return saleMedicine;
    }
 
    public void setSaleMedicine(SaleMedicine saleMedicine) {
        this.saleMedicine = saleMedicine;
    }

   醫院類在賣藥以前,仍是要找醫生看看病的對吧?如今來換一個思路,你以爲醫院不是爲了賣藥的啊,這不科學啊。醫院就是爲了看病的嘛。因此醫院要實現一個看病的接口。先來一個治病接口:ide

public interface Heal {
   
    public void heal();
 
}

再來一個醫院類:測試

public class KindHospital implements Heal{
 
    private SaleMedicine saleMedicine;
    @Override
    public void heal() {
       
        System.out.println("治病");
        saleMedicine.saleMedicine();
       
    }
    public SaleMedicine getSaleMedicine() {
        return saleMedicine;
    }
    public void setSaleMedicine(SaleMedicine saleMedicine) {
        this.saleMedicine = saleMedicine;
    }
 
}

再來一個測主類:this

public class StaticProxy {
   
    public static void main(String[] args) {
       
        proxySaleMedicine();
        System.out.println("---------------------");
        relationSaleMedicine();
    }
   
    public static void proxySaleMedicine()
    {
        Hospital hospital = new Hospital();
        SaleMedicine saleMedicine = new MedicineFactory();
        hospital.setSaleMedicine(saleMedicine);
        hospital.saleMedicine();
    }
   
    public static void relationSaleMedicine()
    {
        KindHospital hospital = new KindHospital();
        SaleMedicine saleMedicine = new MedicineFactory();
        hospital.setSaleMedicine(saleMedicine);
        hospital.heal();
    }
 
}

         看這2個醫院類沒有什麼區別對吧?的確沒有太大的區別,從語義上來講就是側重點不一樣而已。可是對於面向對象編程來講,這是有意義的,使用靜態代理的方式更加符合面向接口編程的原則。它使得共同的服務抽象在了共同的接口中。這讓咱們在消費這一服務的時候有更加靈活的選擇。spa

      靜態代理可能在實際的使用中不是特別多,下面就來看一看動態代理,這很是有用。在springaop就是使用動態代理實現的。代理

2、動態代理

   動態代理相對於靜態代理最大的特色是它不用實現和要代理對象相同的接口,而是在你須要代理的時候把要代理的接口注入進去就能夠了。若是以爲抽象沒有關係,先來看一個例子吧,如今有一個獲取重對象的接口:

public interface HeavyObjectFactory {
 
    public Object getObject(String key);
}

 

而後有一個獲取重對象的類實現了HeavyObjectFactory

public class HeavyObject implements HeavyObjectFactory {
 
    @Override
    public Object getObject(String key) {
        try {
            Thread.sleep(3000);//模擬重對象產生
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "建立重對象";
    }
}

   如今來一個動態生成代理對象的類,它能夠代理實現了HeavyObjectFactory的類,若是要獲取一個重對象就會加上緩存:

public class CacheProxy implements InvocationHandler {
 
    private Map<String,Object> cache = new HashMap<String,Object>();
   
    private static  Method getHeavyMethod;
   
    private static final Class<?> clazz = HeavyObjectFactory.class;
   
    static{
        getHeavyMethod = clazz.getMethods()[0];
    }
    //要被代理的對象,直接使用最頂層的Object類
    private Object target;
   
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
     {
 
        if(getHeavyMethod.equals(method))//若是是獲取重對象的方法就使用緩存
        {
            String key = (String) args[0];
            Object value = cache.get(key);
            if(null == value)
            {
                value = method.invoke(target, args);
                cache.put(key, value);
            }
            return cache;
        }
        return method.invoke(target, args);
    }
    public Object getTarget() {
        return target;
    }
    public void setTarget(Object target) {
        this.target = target;
    }
   
    public Object getCacheProxy(Object target)
    {
        this.target = target;
        Class<? extends Object> clazz = target.getClass();
        //最好注入要代理的接口就能夠了,不要注入全部接口,由於這個是在動態的生成一個對象
        Object object = Proxy.newProxyInstance(clazz.getClassLoader(), 
        clazz.getInterfaces(), this);
        return object;
    }
}

這個類有3個值得注意的地方:

1.  繼承了java.lang.reflect.InvocationHandler這個類
2.  重寫了public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
方法
3.  用Proxy的public static Object newProxyInstance(ClassLoader loader, 
 Class<?>[]interfaces, InvocationHandler h) throws IllegalArgumentException生成要代理的對象。

   先來看ProxynewProxyInstance方法,這個方法有3個參數,第1個是參數是被代理的對象的類加載器,由於代理類是動態生成的因此須要指定類加載器,jvm好加載。第2個是指定要代理的接口。動態生成的代理會代理這些接口的方法。第3個參數是InvocationHandler接口,這個接口只有一個invoke方法就是上面強調的第2點。

   如今好理解了吧,動態生成的代理最終都是調用的

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;

這個方法,這個方法就是把要代理的對象傳進來,把被代理的方法和參數傳進來,就能夠在執行要代理的方法method以前,先作一點本身的事情了對吧。

如今來看一下動態代理的主類,主要測試獲取5次重對象使用緩存代理和不使用緩存代理的差異:

public class DynamicProxy {
   
    public static void main(String[] args) {
       
        HeavyObject heavy = new HeavyObject();
        long start = System.currentTimeMillis();
        for(int i =0;i<5;i++)
            heavy.getObject("key");
        long end = System.currentTimeMillis();
        System.out.println("no cache time:"+(end-start));
       
        CacheProxy cache = new CacheProxy();
        HeavyObjectFactory factory = (HeavyObjectFactory) cache.getCacheProxy(heavy);
       
        start = System.currentTimeMillis();
        for(int i =0;i<5;i++)
            factory.getObject("key");
        end = System.currentTimeMillis();
        System.out.println("caceh time:"+(end-start));
    }
 
}

   其實這種代理再配上註解功能更增強大,好比springCacheable註解。若是須要緩存,除了須要配置一下註解,其餘基本上是透明的。看了spring aop均可以作的事情,你就會感嘆原來動態代理能夠這麼強大。

相關文章
相關標籤/搜索