天天看点

静态代理&动态代理

代理

代理模式的定义:代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用,通俗的来讲代理模式就是我们生活中常见的中介,动态代理和静态代理的区别在于静态代理我们需要手动的去实现目标对象的代理类,而动态代理可以在运行期间动态的生成代理类。

代理模式的作用

  • 功能增强 :在原有的功能上增加了额外的功能
  • 控制访问:代理类不让访问目标类,例如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错误。