天天看點

靜态代理&動态代理

代理

代理模式的定義:代理模式給某一個對象提供一個代理對象,并由代理對象控制對原對象的引用,通俗的來講代理模式就是我們生活中常見的中介,動态代理和靜态代理的差別在于靜态代理我們需要手動的去實作目标對象的代理類,而動态代理可以在運作期間動态的生成代理類。

代理模式的作用

  • 功能增強 :在原有的功能上增加了額外的功能
  • 控制通路:代理類不讓通路目标類,例如B類是A類的代理,C類不能直接通路到A類,隻能通過B類來通路;再例如:商家(廠家的代理)不讓使用者通路廠家

靜态代理

代理類是自己手工實作的,自己建立一個java類來表示代理類

使用靜态代理模拟筆記本購買行為:

使用者:客戶類

商家:代理類,代理某個品牌的筆記本

廠家:目标類

三者關系:使用者 --》商家 --》廠家

實作步驟
  • 建立一個接口,定義銷售筆記本的方法
  • 建立商家類、工廠類,實作上面接口
  • 建立客戶類,購買筆記本

銷售接口

public interface NoteBookSell {
    //根據如購數量來擷取單件的價格
    public int getPrice(Integer num);
}
           

華碩筆記本工廠

//華碩筆記本工廠
public class ASUSfactory implements NoteBookSell {
    @Override
    public int getPrice(Integer num) {
        return num > 200 ? 2499 : 2699;
    }
}
           

淘寶代理商

//淘寶代理商
public class Taobao implements NoteBookSell {
    private static ASUSfactory asuSfactory;
    private static double price;//淘寶出售價格

    static{
        asuSfactory = new ASUSfactory();
        //批發五百件的單價
        int price1 = asuSfactory.getPrice(500);
        //初始化出售價格
        price = price1 + price1 * 0.3;
    }
    @Override
    public int getPrice(Integer num) {
        //如果客戶一次性購買大于等于2件,就每件95折優惠
        return (int)(num >= 2 ? price * 0.95 : price);
    }
}
           

京東代理商

//京東代理商
public class Jingdong implements NoteBookSell {
    private static ASUSfactory asuSfactory;
    private static double price;//京東出售價格

    static{
        asuSfactory = new ASUSfactory();
        //批發五百件的單價
        int price1 = asuSfactory.getPrice(500);
        //初始化出售價格
        price = price1 + price1 * 0.3;
    }
    @Override
    public int getPrice(Integer num) {
        //如果客戶一次性購買大于等于5件,就每件85折優惠
        return (int)(num >= 2 ? price * 0.85 : price);
    }
}
           

main類(模仿客戶類)

public class Main {
    public static void main(String[] args) {
        Taobao taobao = new Taobao();
        int price = taobao.getPrice(1);
        System.out.println("在淘寶上購買一件華碩筆記本單價: "+price);
        int price1 = taobao.getPrice(3);
        System.out.println("在淘寶上購買3件華碩筆記本單價: "+price1);
        
        System.out.println("################################");

        Jingdong jingdong = new Jingdong();
        int price2 = jingdong.getPrice(1);
        System.out.println("在京東上購買一件華碩筆記本單價: "+price2);
        int price3 = jingdong.getPrice(6);
        System.out.println("在京東上購買6件華碩筆記本單價: "+price3);
    }
}
           

運作結果

在淘寶上購買一件華碩筆記本單價: 3248

在淘寶上購買3件華碩筆記本單價: 3086

################################

在京東上購買一件華碩筆記本單價: 3248

在京東上購買6件華碩筆記本單價: 2761

動态代理

動态代理的實作方式

  • jdk動态代理:使用Java反射機制來實作動态代理的功能。寫一個實作InvocationHandler的實作類,用于增強目标類。
  • 反射包:java.lang.reflect,裡面有三個重要的類:InvocationHandler,Method,Proxy。
  • CGLIB動态代理:cglib是第三方提供的工具庫,建立代理對象。
  • cglib的原理是繼承,通過繼承目标類,建立其子類,在子類中重寫父類種同名的方法,實作功能的修改。是以要求目标類以及目标方法不能是final修飾的。在spring(AOP),Hibernate等被應用,CGLIB的代理效率高于JDK代理。
注意:JDK動态代理是代理的接口,是以強制轉換應該轉換為接口,而不是實作類。若強制轉換實作類就會抛出ClassCastException,好比ArrayList與LinkedList實作統一接口List,兩者也不能互相轉換,但都可以向上轉型。

銷售接口

public interface NoteBookSell {
    //根據如購數量來擷取單件的價格
    public int getPrice(Integer num);
}
           

華碩筆記本工廠

//華碩筆記本工廠
public class ASUSfactory implements NoteBookSell {
    @Override
    public int getPrice(Integer num) {
        return num > 200 ? 2499 : 2699;
    }
}
           

InvocationHandler的實作類

//實作反射中的InvocationHandler接口
public class SellNoteBookHandler implements InvocationHandler {

    //需要代理的目标工廠類
    private NoteBookSell target;

    public SellNoteBookHandler(NoteBookSell noteBookSell){
        target = noteBookSell;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //批發500件的單價
        int price = (int)method.invoke(target, 500);
        //初始化出售價格
        double price1 = price + price * 0.3;
        return (int)((int)args[0] >= 2 ? price1 * 0.95 : price1);
    }
}
           

main

public class Main {
    public static void main(String[] args) {
        ASUSfactory asusfactory = new ASUSfactory();
        InvocationHandler handler = new SellNoteBookHandler(asusfactory);
        //建立華碩筆記本代理商
        NoteBookSell proxy = (NoteBookSell)Proxy.newProxyInstance(asusfactory.getClass().getClassLoader(), asusfactory.getClass().getInterfaces(), handler);
        //通過代理執行類中的方法,此時會執行InvocationHandler中的invoke方法
        //public Object invoke(Object proxy, Method method, Object[] args)
        //invoke中的參數是通過jdk自己來傳參; proxy :上述的代理商proxy,method :下面的getPrice方法名擷取的對象,args[0] :下面的參數5
        int price = proxy.getPrice(5);
        System.out.println("通過代理購買一件華碩筆記本單價: "+price);
    }
}
           
注意 如果在InvocationHandler實作類中的invoke方法裡加上 System.out.println(proxy);則會出現java.lang.StackOverflowError錯誤。調試後發現proxy屬于 P r o x y 0 類 , 而 Proxy0類,而 Proxy0類,而Proxy0這個Class是運作時生成的類,網上有一個牛人貼出了$Proxy0的源碼:
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 UserManager {  
        private static Method m1;  
        private static Method m0;  
        private static Method m3;  
        private static Method m2;  
      
        static {  
            try {  
                m1 = Class.forName("java.lang.Object").getMethod("equals",  
                        new Class[] { Class.forName("java.lang.Object") });  
                m0 = Class.forName("java.lang.Object").getMethod("hashCode",  
                        new Class[0]);  
                m3 = Class.forName("cn.edu.jlu.proxy.UserManager").getMethod("addUser",  
                        new Class[0]);  
                m2 = Class.forName("java.lang.Object").getMethod("toString",  
                        new Class[0]);  
            } catch (NoSuchMethodException nosuchmethodexception) {  
                throw new NoSuchMethodError(nosuchmethodexception.getMessage());  
            } catch (ClassNotFoundException classnotfoundexception) {  
                throw new NoClassDefFoundError(classnotfoundexception.getMessage());  
            }  
        }  
      
        public $Proxy0(InvocationHandler invocationhandler) {  
            super(invocationhandler);  
        }  
      
        @Override  
        public final boolean equals(Object obj) {  
            try {  
                return ((Boolean) super.h.invoke(this, m1, new Object[] { obj }))  
                        .booleanValue();  
            } catch (Throwable throwable) {  
                throw new UndeclaredThrowableException(throwable);  
            }  
        }  
      
        @Override  
        public final int hashCode() {  
            try {  
                return ((Integer) super.h.invoke(this, m0, null)).intValue();  
            } catch (Throwable throwable) {  
                throw new UndeclaredThrowableException(throwable);  
            }  
        }  
      
        @Override  
        public final String toString() {  
            try {  
                return (String) super.h.invoke(this, m2, null);  
            } catch (Throwable throwable) {  
                throw new UndeclaredThrowableException(throwable);  
            }  
        }  
      
        @Override  
        public void addUser() {  
            try {  
                super.h.invoke(this, m3, null);  
                return;  
            } catch (Error e) {  
            } catch (Throwable throwable) {  
                throw new UndeclaredThrowableException(throwable);  
            }  
      
        }  
    }  
           

可以看到,調用toString方法的時候,調用了h的invoke方法,而h就是InvocationHandler的執行個體,是以是遞歸調用,是以就會出現上述所說的java.lang.StackOverflowError錯誤。