代理模式(Proxy Pattern)(一)

本文详细介绍了代理模式,包括静态代理和动态代理。通过生活中的代理案例,阐述了代理模式的作用,如减少消费者成本、统一异常处理等。在Java中,静态代理通过接口实现功能增强,动态代理则利用JDK动态代理机制,在运行时动态生成代理类。动态代理允许在不修改原对象代码的情况下,扩展其功能。文章还深入探讨了动态代理的内部实现,包括Proxy类和InvocationHandler接口的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一 简介

1.1 生活当中的代理案例

  • 房屋中介代理

                房屋中介从 包租婆手中获取房源 卖给需要租房的人。

  • 商品代购(比如卖面膜)

                卖面膜的人 从厂家拿面膜 在朋友圈当中宣传 卖给熟人。

1.2 为什么要使用代理

  • 对于消费者而言只需要关注自己需要的商品,可以减少成本,不需要去寻找房源或者渠道,
  • 对于生产者(包租婆,厂家)只需要关注自己的产品,销售只需要让代理者去处理就好 关注自家
  • 对于代理者而言,可以增加类 扩展业务需求 比如收中介费

1.3代理模式在java当中的应用

  1. 统一了异常处理
  2. mybatis使用了代理
  3. spring aop实现了原理
  4. 日志框架

1.4概述

  1. 代理模式(Proxy Pattern):是23中设计模式中的一种,属于结构型的模式,指的是一个对象本身不做实际操作,而是通过其他对象来得到自己想要的结果。
  2. 意义:目标对象只需要关心自己的实现细节,通过代理对象来实现功能的增强,可以扩展目标对象的功能。
  3. 体现了非常重要的编程思想:不能随意修改源码,如果需要修改源码,通过修改代理的方式来实现功能的扩展。

1.5图示

 

 

 

 

  •  元素组成
  1. 接口:定义行为和规范
  2. 被代理类:是目标对象
  3. 代理类:做功能增强
  • 值得注意的点
  1.  用户只需要关注接口功能,而不在乎谁提供了功能。
  2. 接口真正实现这是目标对象,但是他不与用户接触,而是通过代理。
  3. 代理就是Proxy,由于它实现了接口,所以他能够与用户直接接触
  4. 用户调用Proxy的时候,Proxy内部调用了目标对象realSubject。所以Proxy是代理者,它可以增强RealSubject的功能操作。

二 静态代理

2.1 案例 

通过代理模式实现日志操作

2.2 实现案例

假设有一个大商场,商场有很多的柜台,有一个柜台卖茅台酒并且增加品尝和是否购买的需求。我们进行代码的模拟。

2.2.1 创建接口SellWine

首先得有一个接口,通用的接口是代理模式实现的基础。这个接口我们命名为 SellWine,代表卖酒这个行为。

public interface SellWine {
    void maiJiu();
}

2.2.2 创建目标对象MaiTaiJiu类

这个表示真正的对象,它实现了SellWine接口,卖酒的行为有了

(如果按照原始的方式就是在MaoTaiJiu这个对象的每一个方法当中分别添加pinchang()、isGouMai(),就很麻烦 代码冗余,并且如果想要添加增强类的话就需要这个真实对象的源码)

public class MaoTaiJiu implements SellWine{
    public void maiJiu() {
        System.out.println("我卖的是茅台酒!");
    }
    public void baoZhuang(){
        System.out.println("茅台酒的包装");
    }
    public void jiancha(){
        System.out.println("茅台酒日期的检查");
    }
}

2.2.3 创建Proxy GuiTai类

GuiTai就是一个Proxy对象 他有一个maiJiu()方法,不过在调用maiJiu()方法的时候有一些额外的行为比如说pinchang()、isGouMai()

public class GuiTai implements SellWine {
//代理的是MaoTaiJiu这个真实对象 类型固定了 无法代理别的品牌的酒
    private MaoTaiJiu maoTaiJiu;

    public void setMaoTaiJiu(MaoTaiJiu maoTaiJiu){
        this.maoTaiJiu=maoTaiJiu;
    }
  /*  public GuiTai(MaoTaiJiu maoTaiJiu){
        this.maoTaiJiu=maoTaiJiu;
    }*/


    public void maiJiu() {
        pinchang();
        maoTaiJiu.maiJiu();
        goumai(true);
    }

    public void pinchang(){
        System.out.println("客户您可以品尝!");
    }
    public void goumai(boolean isMai){
        if(isMai){
            System.out.println("购买结账!");
        }else{
            System.out.println("在考虑考虑!");
        }

    }
}

2.2.4 创建客户 Client类

public class Client {
    public static void main(String[] args) {
        //真实对象: 茅台酒
        MaoTaiJiu maoTaiJiu = new MaoTaiJiu();
        //代理对象:柜台
        GuiTai guiTai = new GuiTai();

        //通过代理角色 来实现真实对象茅台酒的卖酒
        //1 获取代理的是茅台酒的
        guiTai.setMaoTaiJiu(maoTaiJiu);
        guiTai.maiJiu();

    }
}

结果:

现在可以看到,代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。值得注意的是,代理类和被代理类应该共同实现一个接口,或者是共同继承某个类。

上面介绍的是静态代理的内容,为什么叫做静态呢?因为它的类型是事先预定好

 

三 动态代理

上一节代码中 GuiTai类是代理,我们需要手动编写代码让 GuiTai 实现 SellWine 接口,而在动态代理中,我们可以让程序在运行的时候自动在内存中创建一个实现 SellWine 接口的代理,而不需要去定义 GuiTai 这个类。这就是它被称为动态的原因。

3.1 jdk动态代理

3.1.1 案例

3.1.1.1创建接口SellWine

可以理解为卖酒的许可证

public interface SellWine {
    void maiJiu();
   
}
3.1.1.2创建真实对象MaoTaiJiu类
public class MaoTaiJiu implements SellWine{
    public void maiJiu() {
        System.out.println("我卖的是茅台酒!");
    }
    public void baoZhuang(){
        System.out.println("茅台酒的包装");
    }
    public void jiancha(){
        System.out.println("茅台酒日期的检查");
    }
}
3.1.1.3创建ProxyInvocationHandler类
//柜台A代理卖酒
public class GuiTaiA implements InvocationHandler {

    //被代理类的接口

   /* private SellWine sellWine;*/
    //升级一下 卖的酒的品牌不止一家
    private Object pingpai;

    //增强类
    private PinChang pinChang;

    //传入InvocationHandler当中
   /* public void setSellWine(SellWine sellWine,PinChang pinChang){
        this.sellWine=sellWine;
        this.pinChang=pinChang;
    }*/

    public GuiTaiA() {
    }

    public GuiTaiA(PinChang pinChang) {
        this.pinChang = pinChang;
    }

    public GuiTaiA(Object pingpai) {
        this.pingpai = pingpai;
    }

    public GuiTaiA(Object pingpai, PinChang pinChang) {
        this.pingpai = pingpai;
        this.pinChang = pinChang;
    }



    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object ret=null;
        if(pinChang!=null){
            pinChang.pinchang();
        }
        System.out.println("开始售卖,柜台是"+this.getClass().getSimpleName());
        ret =method.invoke(pingpai,args);
        if(pinChang!=null){
            pinChang.goumai(true);
        }
        System.out.println("售卖结束");
        return ret;
    }
}
3.1.1.4客户
//有额外的功能
    public static void main(String[] args) {
        //真实对象: 茅台酒
        MaoTaiJiu maoTaiJiu = new MaoTaiJiu();
        //增强类
        PinChang pinChang=new PinChang();
        //要代理哪一个真实对象通过ProxyInvocationHandler
        GuiTaiA pih = new GuiTaiA(maoTaiJiu,pinChang);// 获取代理的是茅台酒的
        //要强制转换为接口类型
        SellWine proxy = (SellWine) Proxy.newProxyInstance(maoTaiJiu.getClass().getClassLoader(),
                                                            maoTaiJiu.getClass().getInterfaces(),
                                                            pih);

        proxy.maiJiu();

    }

与静态代理相比,我们并没有像静态代理那样为SellWine接口实现一个代理实现类,而是弄了一个invocationHandler类 (代表这一类的代理类 可以利用里面的newProxyInstance()来创建代理类的实例)

3.1.2jdk动态代理的语法

A Proxy类
public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)

参数:

  1. ClassLoader:类加载器
  2. interface:被代理的接口 (抽象角色的接口)
  3. h:一个InvocationHandler对象
B InvocationHandler类

InvocationHandler是一个接口。

每个代理的实例都有与之关联的一个InvocationHandler实现类,如果代理的方法被调哦那个,那么代理便会同志和转发给内部的InvocationHandler实现类,由他决定处理。

public interface InvocationHandler {

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

InvocationHandler接口内部只有一个invoke()方法,正是因为这个方法决定了怎么样处理代理传递过来的方法调用。

参数:

  1. proxy:代理对象
  2. method:代理对象调用的方法(通过这个method去寻找真实对象的Class的)
  3. args:调用的方法当中的参数

因为:proxy动态产生的代理会调用InvocationHandler实现类,所以InvocationHandler实现类是实际执行者!

3.1.3 加大难度 真实对象多个!! 

我们不仅要卖茅台酒,还要卖五粮液!!!!

添加WuLiangYe类

public class WuLiangYe implements SellWine {

    public void maiJiu() {
        System.out.println("我卖的是五粮液");
    }

}

客户端

 public static void main(String[] args) {
            //真实对象: 茅台酒
            MaoTaiJiu maoTaiJiu = new MaoTaiJiu();
            //真实对象:五粮液
            WuLiangYe wuLiangYe = new WuLiangYe();
            //增强类
            PinChang pinChang = new PinChang();

            GuiTaiA pihM = new GuiTaiA(maoTaiJiu);// 获取代理的是茅台酒的 这一块空间有标签是放茅台酒的
            GuiTaiA pihW = new GuiTaiA(wuLiangYe);// 获取代理的是五粮液的 这一块空间有标签是放五粮液的

            SellWine maoTaiJiuProxy = (SellWine) Proxy.newProxyInstance(MaoTaiJiu.class.getClassLoader(),
                    MaoTaiJiu.class.getInterfaces(), pihM
            );
            SellWine wuLiangYeProxy = (SellWine) Proxy.newProxyInstance(MaoTaiJiu.class.getClassLoader(),
                    MaoTaiJiu.class.getInterfaces(), pihW
            );//注意反射房中获取Class的几种方法 不要大小写书写错误

            maoTaiJiuProxy.maiJiu();
            wuLiangYeProxy.maiJiu();

    }

}

结果展示:

是一类代理角色,代理角色实例1卖茅台 ,代理角色实例2卖五粮液 !!!这一类的代理角色都是代理SellWine接口的

 3.1.4 加大难度 多个抽象角色(比如还想卖烟)

柜台A 还想要卖烟

3.1.4.1创建SellYan接口
public interface SellYan {
    void MaiYan();
}

3.1.4.2创建烟的真实角色

public class ZhongHua implements SellYan {

    public void MaiYan() {
        System.out.println("售卖的是中华烟,可以扫描条形码查证。");
    }
}

3.1.4.3 客户

public class Client {
public static void main(String[] args) {
    //真实对象
    MaoTaiJiu maoTaiJiu = new MaoTaiJiu();
    WuLiangYe wuLiangYe = new WuLiangYe();
    ZhongHua zhongHua = new ZhongHua();
    //增强类
    PinChang pinChang = new PinChang();
    //代理卖酒的接口

    GuiTaiA m = new GuiTaiA(maoTaiJiu);
    GuiTaiA w = new GuiTaiA(wuLiangYe);
    SellWine maotaijiuProxy = (SellWine) Proxy.newProxyInstance(MaoTaiJiu.class.getClassLoader(), MaoTaiJiu.class.getInterfaces(), m);//卖茅台酒
    SellWine wuliangyeProxy = (SellWine)Proxy.newProxyInstance(MaoTaiJiu.class.getClassLoader(), MaoTaiJiu.class.getInterfaces(), w);//卖五粮液

    maotaijiuProxy.maiJiu();
    wuliangyeProxy.maiJiu();
    //代理卖烟的接口
    GuiTaiA z = new GuiTaiA(zhongHua);
    SellYan zhonghuayanProxy = (SellYan) Proxy.newProxyInstance(ZhongHua.class.getClassLoader(), ZhongHua.class.getInterfaces(), z);//卖中华烟的
    zhonghuayanProxy.MaiYan();

}}

结果展示:

 结果符合预期。大家仔细观察一下代码,同样是通过 Proxy.newProxyInstance() 方法,却产生了 SellWine 和 SellCigarette 两种接口的实现类代理,这就是动态代理的魔力

四 动态代理的秘密

我们会疑惑为什么Proxy能够动态长生不同接口类型的代理?

猜测可能是因为通过传入的接口==》然后通过反射机制生成了一个接口实例!
比如 SellWine 是一个接口,那么 Proxy.newProxyInstance() 内部肯定会有

new SellWine();

这样相同作用的代码,不过它是通过反射机制创建的。那么事实是不是这样子呢?直接查看它们的源码好了。需要说明的是,我当前查看的源码是 1.8 版本。

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

        final Class<?>[] intfs = interfaces.clone();


        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {


            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;
                    }
                });
            }

            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);
        }
    }

newProxyInstance 的确创建了一个实例,它是通过 cl 这个 Class 文件的构造方法反射生成。cl 由 getProxyClass0() 方法获取

private static Class<?> getProxyClass0(ClassLoader loader,
                                       Class<?>... interfaces) {
    if (interfaces.length > 65535) {
        throw new IllegalArgumentException("interface limit exceeded");
    }

    // If the proxy class defined by the given loader implementing
    // the given interfaces exists, this will simply return the cached copy;
    // otherwise, it will create the proxy class via the ProxyClassFactory
    return proxyClassCache.get(loader, interfaces);
}

直接通过缓存获取,如果获取不到,注释说会通过 ProxyClassFactory 生成。

/**
     * A factory function that generates, defines and returns the proxy class given
     * the ClassLoader and array of interfaces.
     */
    private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    {
        // Proxy class 的前缀是 “$Proxy”,
        private static final String proxyClassNamePrefix = "$Proxy";

        // next number to use for generation of unique proxy class names
        private static final AtomicLong nextUniqueNumber = new AtomicLong();

        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            for (Class<?> intf : interfaces) {
                /*
                 * Verify that the class loader resolves the name of this
                 * interface to the same Class object.
                 */
                Class<?> interfaceClass = null;
                try {
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }
                if (interfaceClass != intf) {
                    throw new IllegalArgumentException(
                        intf + " is not visible from class loader");
                }
                /*
                 * Verify that the Class object actually represents an
                 * interface.
                 */
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
                /*
                 * Verify that this interface is not a duplicate.
                 */
                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
                }
            }

            String proxyPkg = null;     // package to define proxy class in
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

            /*
             * Record the package of a non-public proxy interface so that the
             * proxy class will be defined in the same package.  Verify that
             * all non-public proxy interfaces are in the same package.
             */
            for (Class<?> intf : interfaces) {
                int flags = intf.getModifiers();
                if (!Modifier.isPublic(flags)) {
                    accessFlags = Modifier.FINAL;
                    String name = intf.getName();
                    int n = name.lastIndexOf('.');
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    } else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException(
                            "non-public interfaces from different packages");
                    }
                }
            }

            if (proxyPkg == null) {
                // if no non-public proxy interfaces, use com.sun.proxy package
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }

            /*
             * Choose a name for the proxy class to generate.
             */
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;

            /*
             * Generate the specified proxy class.
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                /*
                 * A ClassFormatError here means that (barring bugs in the
                 * proxy class generation code) there was some other
                 * invalid aspect of the arguments supplied to the proxy
                 * class creation (such as virtual machine limitations
                 * exceeded).
                 */
                throw new IllegalArgumentException(e.toString());
            }
        }
    }

这个类的注释说,通过指定的 ClassLoader 和 接口数组 用工厂方法生成 proxy class。 然后这个 proxy class 的名字是

// Proxy class 的前缀是 “$Proxy”,
private static final String proxyClassNamePrefix = "$Proxy";

long num = nextUniqueNumber.getAndIncrement();

String proxyName = proxyPkg + proxyClassNamePrefix + num;

所以,动态生成的代理类名称是包名+$Proxy+id序号

生成的过程,核心代码如下:

byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);


return defineClass0(loader, proxyName,
                    proxyClassFile, 0, proxyClassFile.length);

这两个方法,我没有继续追踪下去,defineClass0() 甚至是一个 native 方法。我们只要知道,动态创建代理这回事就好了。

现在我们还需要做一些验证,我要检测一下动态生成的代理类的名字是不是包名+$Proxy+id序号

public static void main(String[] args) {
    //真实对象
    MaoTaiJiu maoTaiJiu = new MaoTaiJiu();
    WuLiangYe wuLiangYe = new WuLiangYe();
    ZhongHua zhongHua = new ZhongHua();
    //增强类
    PinChang pinChang = new PinChang();
    //代理卖酒的接口

    GuiTaiA m = new GuiTaiA(maoTaiJiu);
    GuiTaiA w = new GuiTaiA(wuLiangYe);
    SellWine maotaijiuProxy = (SellWine) Proxy.newProxyInstance(MaoTaiJiu.class.getClassLoader(), MaoTaiJiu.class.getInterfaces(), m);//卖茅台酒
    SellWine wuliangyeProxy = (SellWine)Proxy.newProxyInstance(MaoTaiJiu.class.getClassLoader(), MaoTaiJiu.class.getInterfaces(), w);//卖五粮液

    maotaijiuProxy.maiJiu();
    wuliangyeProxy.maiJiu();
    //代理卖烟的接口
    GuiTaiA z = new GuiTaiA(zhongHua);
    SellYan zhonghuayanProxy = (SellYan) Proxy.newProxyInstance(ZhongHua.class.getClassLoader(), ZhongHua.class.getInterfaces(), z);//卖中华烟的
    zhonghuayanProxy.MaiYan();


    System.out.println("maotaijiuProxy class name:"+maotaijiuProxy.getClass().getName());
    System.out.println("wuliangyeProxy class name:"+wuliangyeProxy.getClass().getName());
    System.out.println("zhonghuayanProxy class name:"+zhonghuayanProxy.getClass().getName());
}

结果展示:

 

 

SellWine 接口的代理类名是:com.sun.proxy.$Proxy0
SellYan 接口的代理类名是:com.sun.proxy.$Proxy1

这说明动态生成的 proxy class 与 Proxy 这个类同一个包。

下面用一张图让大家记住动态代理涉及到的角色。

 红框中 $Proxy0 就是通过 Proxy 动态生成的。
$Proxy0 实现了要代理的接口。
$Proxy0 通过调用 InvocationHandler 来执行任务。

总结:代理的作用

主要作用,还是在不修改被代理对象的源码上,进行功能的增强。

这在 AOP 面向切面编程领域经常见。

  1. 代理分为静态代理和动态代理两种。
  2. 静态代理,代理类需要自己编写代码写成。
  3. 动态代理,代理类通过 Proxy.newInstance() 方法生成。
  4. 不管是静态代理还是动态代理,代理与被代理者都要实现两样接口,它们的实质是面向接口编程。
  5. 静态代理和动态代理的区别是在于要不要开发者自己定义 Proxy 类。
  6. 动态代理通过 Proxy 动态生成 proxy class,但是它也指定了一个 InvocationHandler 的实现类。
  7. 代理模式本质上的目的是为了增强现有代码的功能。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值