设计模式之代理模式

设计模式之代理模式

代理模式的定义:代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。这种类型的设计模式属于结构型模式。
比如一个餐厅要上线外卖功能,第一种方法是有顾客自己打电话到餐厅,进行点餐。但可能很少人知道能餐厅上线了外卖功能。第二就是在美团上上线外卖功能上美团,美团帮餐厅去接单。

代理又分两种,静态代理和动态代理;
静态代理在程序运行前,代理类已经提前写好。
动态代理则是在程序运行时,动态的创建代理类。其中又分为JDK动态代理和CGLIB动态代理,JDK动态代理是基于接口进行代理的,被代理类必须实现自一个接口,没有接口的类可以使用CGLIB动态代理。

静态代理

代码样例如下:

Order.java

public interface Order {
    void order();
}
MeiTuan.java
```java
public class MeiTuan implements Order {

    private Restaurant restaurantA  = new Restaurant();

    public void order() {
        System.out.println("收到一笔A餐厅的外卖订单");
        restaurantA.order();
    }
}

Restaurant.java

public class Restaurant implements Order {
    public void order() {
        System.out.println("A餐厅,收到来自美团的订单");
    }
}

Main.java

public class Main {
    public static void main(String[] args) {
        //打开美团客户端
        Order order = new MeiTuan();
        //下单
        order.order();
    }
}
JDK动态代理

接口Order.Java

public interface Order {
    void order();
}

Restaurant.java

public class Restaurant implements Order {
    public void order() {
        System.out.println("A餐厅,收到来自美团的订单");
    }
}

Main.java

public class Main {
    public static void main(String[] args)  {
        Restaurant restaurant = new Restaurant();
        Order proxyMeietuan = (Order) Proxy.newProxyInstance(Order.class.getClassLoader(), new
                                Class[]{Order.class}, new MyInvovationHandler(restaurant));
        proxyMeietuan.order();
    }
}

Proxy.java

public class MyInvovationHandler implements InvocationHandler {

    private Restaurant restaurant;

    public Proxy (Restaurant restaurant) {
        this.restaurant = restaurant;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before order");
        method.invoke(restaurant,args);
        System.out.println("after order");
        return null;
    }
}

如何查看动态代理所生成的类
使用txt,把引号内容换成自己java_home对应的路径,修改完毕后把文件后缀改成.bat

java -classpath "D:\Java\jdk1.8.0_261\lib\sa-jdi.jar" sun.jvm.hotspot.HSDB

然后打开.bat文件,点击attach to hotSpot process
在这里插入图片描述
然后输入进程号,如不清楚进程号,可以输入jps查询

输入进程号后点击class broswer
在这里插入图片描述
找到proxy,在这里插入图片描述
再点击
在这里插入图片描述
在这里插入图片描述
然后你就能看到生成了一个文件夹,里面就有一个class文件
在这里插入图片描述
把文件拉入idea,就可以看到生成的动态代理类了

public final class $Proxy0 extends Proxy implements Order {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    public final void order() {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public $Proxy0(InvocationHandler var1) {
        super(var1);
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("com.design.proxy.Order").getMethod("order");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }

    public final boolean equals(Object var1) {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
}

可以看到,所生成的proxy类继承于Proxy,由于java是单继承的,所以jdk动态代理只能用在接口上面。

结合生成出来的动态代理代码,分析一下调用order的流程。

首先从proxy的创建看起

        Order proxyMeietuan = (Order) Proxy.newProxyInstance(Order.class.getClassLoader(), new
                                Class[]{Order.class}, new MyInvovationHandler(restaurant));

点进去newProxyInstance方法

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }
		创建动态代理的classClass<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }
//拿到动态代理proxy的构造方法
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            //通过构造方法,把invocationHandler赋值进去
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

大致的步骤就有3个,先生成动态代理类,然后拿到他的构造方法,调用构造方法把invocationHandler赋值进去,并放回通过构造方法得到的动态代理对象。

再结合动态代理所生成的$proxy的源码

 public final void order() {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

 m3 = Class.forName("com.design.proxy.Order").getMethod("order");

可以看到,调用proxyMeietuan.order方法,就会进入到order,h就是刚刚传过来的invocationHandler,所以就会先调用MyInvovationHandler 的invoke方法

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before order");
        method.invoke(restaurant,args);
        System.out.println("after order");
        return null;
    }

method就是刚刚传进来的m3,restaurant是通过invocationHandler的构造器传进来的,这样通过反射,就可以调用restaurant的order方法,来实现动态代理。

动态代理在很多场景都用到了,例如spring的aop,mybatis里面通过dao接口就能调用mapper的sql,还有feign的远程调用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值