代理模式初探

代碼地址:https://git.oschina.net/wizards/jdk1.8Test.gitjava

簡介

代理,proxy;生活中例子很常見,好比房產中介,租客和房主實際上不須要見面,只須要中介就能完成租房活動.這樣租客和房主都能節省時間,保障人身安全;固然,他們須要付一些中介費.但只要中介的費用在接受範圍以內,一切仍是值得的. 在java開發中會用一些狀況,須要咱們採用這種模式.即屏蔽掉全部與咱們目標不相關的方法,屬性,常量,只獲取全部與目標相關的方法和屬性.git

簡單構造以下:spring

package com.wangge.proxy.simple;

public class SimpleProxy {
  private Shop shop = new Shop();
  public void sell() {
    shop.sellFood();
  }
}

class Shop {
  void sellFood() {
    System.out.println("賣出一個吃的");
  }
}

這是一個簡單的代理模式,也實現了功能的封裝.數組

抽象角色:聲明真實對象和代理對象的共同接口;安全

代理角色:代理對象角色內部含有對真實對象的引用,從而能夠操做真實對象,同時代理對象提供與真實對象相同的接口以便在任什麼時候刻都能代替真實對象。同時,代理對象能夠在執行真實對象操做時,附加其餘的操做,至關於對真實對象進行封裝。ide

真實角色:代理角色所表明的真實對象,是咱們最終要引用的對象函數

目前的代理通常分爲靜態代理和動態代理;性能

靜態代理

靜態代理也叫接口代理.此時代理的角色是接口.this

代碼以下;.net

package com.wangge.proxy.interfaceCase3;

public interface Waiter {
  public void service(int sum);
}

package com.wangge.proxy.interfaceCase3;

public class HotelWaiter implements Waiter {
  
  @Override
  public void service(int xx) {
    System.out.println("我要訂" + xx+"個房間!!");
  }
  
}


package com.wangge.proxy.interfaceCase3;

public class RestaurantWaiter implements Waiter{

  @Override
  public void service(int xx) {
    System.out.println("給你"+xx+"道菜!!");
  }
  
}


package com.wangge.proxy.interfaceCase3;

import java.util.HashMap;
import java.util.Map;

/**
 * 標準代理模式<br/>
 * date: 2016年11月10日 上午9:51:40 <br/>
 * 
 * @author yangqc
 * @version
 * @since JDK 1.8
 */
public class WaiterProxy implements Waiter {
  private Waiter waiter;
    
  public WaiterProxy(Waiter waiter) {
    super();
    this.waiter = waiter;
  }

  @Override
  public void service(int sum) {
    waiter.service(sum);
    
  }
  
  public static void main(String[] args) {
    Waiter waiter=new WaiterProxy(new HotelWaiter());
    waiter.service(2);
  }
}

接口Waiter,Waiter的兩個實現類. WaiterProxy是Waiter的代理類.經過Waiter的實現類來建立WaiterProxy的代理類,經過代理類來實現接口的方法.

動態代理

Java動態代理類位於Java.lang.reflect包下,通常主要涉及到如下兩個類:

(1). Interface InvocationHandler:該接口中僅定義了一個方法Object:invoke(Object obj,Method method, Object[] args)。在實際使用時,第一個參數obj通常是指代理類,method是被代理的方法,如上例中的request(),args爲該方法的參數數組。 這個抽象方法在代理類中動態實現。

(2).Proxy:該類即爲動態代理類,做用相似於上例中的ProxySubject,其中主要包含如下內容: Protected Proxy(InvocationHandler h):構造函數,估計用於給內部的h賦值。

Static Class getProxyClass (ClassLoader loader, Class[] interfaces):得到一個代理類,其中loader是類裝載器,interfaces是真實類所擁有的所有接口的數組。

Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):返回代理類的一個實例,返回後的代理類能夠看成被代理類使用(可以使用被代理類的在Subject接口中聲明過的方法)。

所謂 Dynamic Proxy是這樣一種class:它是在運行時生成的class,在生成它時你必須提供一組interface給它,而後該class就宣稱它實現了這些 interface。你固然能夠把該class的實例看成這些interface中的任何一個來用。固然啦,這個Dynamic Proxy其實就是一個Proxy,它不會替你做實質性的工做,在生成它的實例時你必須提供一個handler,由它接管實際的工做。(參見文獻3)

在使用動態代理類時,咱們必須實現InvocationHandler接口,以第一節中的示例爲例:

代碼: //抽象角色(以前是抽象類,此處應改成接口):

public interface Subject{
     public void request();
} 

//具體角色RealSubject:實現了Subject接口的request()方法。
public class RealSubject implements Subject{ 
     public RealSubject(){

     }
     public void request(){ 
           System.out.println("From real subject."); 
     } 
}

//代理角色:
import java.lang.reflect.Method;
import java.lang.reflect.InvocationHandler;
public class DynamicSubject implements InvocationHandler{
     private Object sub; 
     public DynamicSubject(Object sub){            
           this.sub = sub;
     }
     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
         System.out.println("before calling " + method); 
         method.invoke(sub,args); 
         System.out.println("after calling " + method); 
         return null; 
     }
}

客戶端代碼以下

mport java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class Client{ 
  static public void main(String[] args) throws Throwable{
  RealSubject rs = new RealSubject(); //在這裏指定被代理類
  InvocationHandler ds = new DynamicSubject(rs); //初始化代理類
  Class cls = rs.getClass();
  //如下是分解步驟
  /*
  Class c = Proxy.getProxyClass(cls.getClassLoader(),cls.getInterfaces());
  Constructor ct=c.getConstructor(new Class[]{InvocationHandler.class});
  Subject subject =(Subject) ct.newInstance(new Object[]{ds});
*/

//如下是一次性生成

  Subject subject = (Subject) Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(),ds);
  subject.request();
}

經過這種方式,被代理的對象(RealSubject)能夠在運行時動態改變,須要控制的接口(Subject接口)能夠在運行時改變,控制的方式(DynamicSubject類)也能夠動態改變,從而實現了很是靈活的動態代理關係。

可是,JDK的動態代理依靠接口實現,若是有些類並無實現接口,則不能使用JDK代理,這就要使用cglib動態代理了。

Cglib動態代理

JDK的動態代理機制只能代理實現了接口的類,而不能實現接口的類就不能實現JDK的動態代理,cglib是針對類來實現代理的,他的原理是對指定的目標類生成一個子類,並覆蓋其中方法實現加強,但由於採用的是繼承,因此不能對final修飾的類進行代理。不能重寫final,static,private,protected,default 修飾的方法;

package com.wangge.proxy.cglibProxy;

public class Sayhello {
  public void hello() {
    System.out.println("say hello!!");
  }
  
  final public void sayBye() {
    System.out.println("Bye Bye!!");
  }
  
  static void eat() {
    System.out.println("eating food !!");
  }
  
  public static void main(String[] args) {
    CglibProxy proxy = new CglibProxy();
    Sayhello say = (Sayhello) proxy.getProxy(Sayhello.class);
    say.hello();
    //子類沒法重寫父類的final方法和static方法
    say.sayBye();//不會執行代理邏輯
    say.eat();//不會執行代理邏輯
    //沒法繼承final類,拋出異常:java.lang.IllegalArgumentException
//    SayBye bye = (SayBye) proxy.getProxy(SayBye.class);
//    bye.sayBye();
  }
}

package com.wangge.proxy.cglibProxy;

public final class SayBye {
  public void sayBye(){
    System.out.println("I can ever leave !");
  }
}


package com.wangge.proxy.cglibProxy;

import java.lang.reflect.Method;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

public class CglibProxy implements MethodInterceptor{  
  private Enhancer enhancer = new Enhancer();  
  public Object getProxy(Class<?> clazz){  
   //設置須要建立子類的類  
   enhancer.setSuperclass(clazz);  
   enhancer.setCallback(this);  
   //經過字節碼技術動態建立子類實例  
   return enhancer.create();  
  }  
  //實現MethodInterceptor接口方法  
  @Override
  public Object intercept(Object obj, Method method, Object[] args,  
    MethodProxy proxy) throws Throwable {  
   System.out.println("前置代理");  
   //經過代理類調用父類中的方法  
   Object result = proxy.invokeSuper(obj, args);  
   System.out.println("後置代理");  
   return result;  
  }
  
 }

CGLib建立的動態代理對象性能比JDK建立的動態代理對象的性能高很多,可是CGLib在建立代理對象時所花費的時間卻比JDK多得多,因此對於單例的對象,由於無需頻繁建立對象,用CGLib合適,反之,使用JDK方式要更爲合適一些。同時,因爲CGLib因爲是採用動態建立子類的方法,對於final方法,沒法進行代理。

使用場景

代理模式的其餘應用: 1,遠程代理,爲一個對象在不一樣的地址空間提供局部表明。這樣能夠隱藏一個對象存在於不一樣地址空間的事實。

2,虛擬代理,根據須要建立開銷很大的對象。經過它來存放實例化須要很長時間的真實對象。例如,網頁中在圖片出來之前現出來文字。

3,安全代理,用來控制真實對象訪問時的權限。

4,智能代理,是指當調用真實的對象時,代理處理另一些事。如AOP 5.與bean做用域相關,看成用域時間長的bean引用做用域引用時間短的bean,須要使用代理. 詳見https://my.oschina.net/u/1590027/blog/731904文中Scoped beans as dependencies段.

相關文章
相關標籤/搜索