设计模式之代理模式

106 阅读3分钟

1 简述

代理模式的思想在生活中还是很常见。
比如,老王想买美版的苹果机,但他不会英语,出国买不方便,于是就找代购商,购买美版的苹果机。
这个过程中,代购商就是代理对象,老王通过代购商,购买了自己想要的东西。
这就是代理模式的典型体现。
代理模式又分静态代理动态代理,下面详细来看看。

2 静态代理

public class ProxyDemo1 {

    public static void main(String[] args) {
        User laoWang = new User("老王");
        IPhoneProxy iPhoneProxy = new IPhoneProxy(laoWang);
        iPhoneProxy.buy();
    }

    public static class User implements Buy {
        private String name;
        public User(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }


        @Override
        public void buy() {
            System.out.println(this.name + "想买东西");
        }
    }

    public static class IPhoneProxy implements Buy {

        User user;

        public IPhoneProxy(User user) {
            this.user = user;
        }

        @Override
        public void buy() {
            user.buy();
            System.out.println("苹果机代购商帮" + user.getName() + "购买苹果机");
        }
    }

    public interface Buy {
        public void buy();
    }

}
// ---- 打印 ----
老王想买东西
苹果机代购商帮老王购买苹果机

从上述代码可以看出,
IPhoneProxy代理类实现了接口Buy,老王想要买苹果机,他自己无法去国外购买,就通过代理商买到了手机。
这就是静态代理模式的简单运用。
我们更进一步,还可以加入其他代理类,这个老王就可以通过这些代理,买到很多买不到的东西。

public static class ComputerProxy implements Buy {
	...
}
...

但这样实现一堆代理类也会造成使用上的不变。如果老王想买苹果机、苹果电脑等等,就需要写多行buy()。

可以改造一下,实现嵌套。

package com.yzeng.proxy;

public class ProxyDemo1 {

    public static void main(String[] args) {
        User laoWang = new User("老王");

        IPhoneProxy iPhoneProxy = new IPhoneProxy(new MacProxy(laoWang));
        iPhoneProxy.buy();
    }

    public static class User implements Buy {
        private String name;
        public User(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }


        @Override
        public void buy() {
            System.out.println(this.name + "想买东西");
        }
    }

    public static class IPhoneProxy implements Buy {

        Buy buy;

        public IPhoneProxy(Buy buy) {
            this.buy = buy;
        }

        @Override
        public void buy() {
            buy.buy();
            System.out.println("苹果机代购商帮购买苹果机");
        }
    }

    public static class MacProxy implements Buy {

        Buy buy;

        public MacProxy(Buy buy) {
            this.buy = buy;
        }

        @Override
        public void buy() {
            buy.buy();
            System.out.println("Mac代购商帮购买Mac");
        }
    }

    public interface Buy {
        public void buy();
    }

}
// ---- 打印 ----
老王想买东西
Mac代购商帮购买Mac
苹果机代购商帮购买苹果机

不知道,各位读者看到上面的代码有没有什么想法?
非常像装饰器模式

这样的嵌套形式貌似还是显得冗余,我们是否可以让代购苹果机的,还能去代购Mac或其他商品?
下面看看动态代理模式。

3 动态代理

利用反射机制在运行时创建代理类。

public static void main(String[] args) {
    User laoWang = new User("老王");

    BuyIphone buyIphone = (BuyIphone)Proxy.newProxyInstance(User.class.getClassLoader(),
                                                            laoWang.getClass().getInterfaces(),
                                                            new IPhoneHandler(laoWang));

    BuyMac buyMac = (BuyMac)Proxy.newProxyInstance(User.class.getClassLoader(),
                                                   laoWang.getClass().getInterfaces(),
                                                   new IPhoneHandler(laoWang));

    buyIphone.buyIphone();
    buyMac.buyMac();
}

public static class User implements BuyIphone, BuyMac {
    private String name;
    public User(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    @Override
    public void buyIphone() {
        System.out.println(this.name + "想买iphone");
    }

    @Override
    public void buyMac() {
        System.out.println(this.name + "想买Mac");
    }
}

public static class IPhoneHandler implements InvocationHandler {

    User user;

    public IPhoneHandler(User user) {
        this.user = user;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object o = null;
        if (method.getName().endsWith("buyIphone")) {
            o = method.invoke(user, args);
            System.out.println("苹果机代购商帮购买苹果机");
        } else if (method.getName().endsWith("buyMac")) {
            o = method.invoke(user, args);
            System.out.println("苹果机代购商帮购买Mac");
        }
        return o;
    }
}

public interface BuyIphone {
    public void buyIphone();
}

public interface BuyMac {
    public void buyMac();
}

通过Proxy.newProxyInstance在运行时生成了代理类。
动态代理的原理也比较简单。我们可以通过配置看到生成的代理类源码。

// 旧版
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
// 新版
// System.getProperties().put(“jdk.proxy.ProxyGenerator.saveGeneratedFiles”, “true”);

企业微信截图_7830aa08-96ae-46b9-9f70-253dc4772deb.png
运行上面的Java代码,就可以在com.sun.proxy目录下得到生成的代理类$Proxy0

public final class $Proxy0 extends Proxy implements BuyIphone, BuyMac {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m4;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }
	...
    public final void buyIphone() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    ...
    public final void buyMac() throws  {
        try {
            super.h.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    ...
    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("com.yzeng.proxy.ProxyDemo2$BuyIphone").getMethod("buyIphone");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m4 = Class.forName("com.yzeng.proxy.ProxyDemo2$BuyMac").getMethod("buyMac");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

从源码中可以看到,
super.h.invoke(this, m4, (Object[])null);
就是执行的我们传入的IphoneHandler方法,并通过发射传入了该接口。

4 总结

不管是动态代理还是静态代理,思想上都是在代理“客户”的行为。
另外,有了动态代理,我们就可以在方法执行前,和执行后分别嵌入更多的逻辑。
例如,Spring的AOP就是通过动态代理来实现切面编程的。