设计模式:代理模式

什么是代理模式?
为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
以上为百度百科对代理模式的定义。嗯,有点拗口,也不太好理解。那么我们换一个通俗点的例子:我们去餐厅吃饭,点了一盘大盘鸡。做这道菜的肯定是餐厅的厨师对吧,但是厨师人家做的技术活,才不想直接跟这些个食客当面聊呢,人家也是很高傲的,所以呢就由餐厅的服务员与食客做对接,由服务员将我们的需求传递给厨师,厨师做好了之后在由服务员传递到食客桌上。
这中间有3个角色:1、食客 2、服务员 3、厨师
提需求的是食客,正真做菜的是厨师,服务员只在中间起到了传递信息的角色。但请主意,如果只是这么简单那你可就太小看这个服务员了。假设你是带着你的现女友去吃饭,而服务员恰好是你的前女友,那么可能会出现点啥菜都没有的情况对不?厨师做好了菜服务员可能帮你加点葱啊啥的不?对了,这其实才是代理模式的真正精髓:预处理消息、过滤消息、添加额外的功能。
代理模式分为静态代理和动态代理,静态代理比较简单但缺乏灵活性。动态代理编写比较复杂,相对比较灵活,性能没静态代理高。
静态代理:

/**
 * 接口
 */
public interface HelloInterface {
    void sayHello();
}

/**
 * 委托类  真正干活的角色
 */
public class Hello implements HelloInterface{
    @Override
    public void sayHello() {
        System.out.println("Hello World!");
    }
}
/**
 * 代理类
 */
public class HelloProxy implements HelloInterface{
    private HelloInterface helloInterface = new Hello();
    @Override
    public void sayHello() {
        System.out.println("Hello do before");
        helloInterface.sayHello();
        System.out.println("Hello do after");
    }
}

代码非常简单,在代理类中我们可以进行额外的一些处理。执行结果如下。

Hello do before
Hello World!
Hello do after

好了,我们看完了静态代理在看下动态代理。但在看动态代理之前我们先看以下的代码。

//测试生成.class文件
        File file = new File("E:\\CalmJava\\Proxy\\src\\com\\calm\\proxy\\HelloImpl.class");
        byte[] helloImpls = ProxyGenerator.generateProxyClass("HelloImpl", new Class[]{HelloInterface.class});
        try {
            FileOutputStream outputStream = new FileOutputStream(file);
            outputStream.write(helloImpls);
            outputStream.flush();
            outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

额,请忽略掉这个绝对路径,我也只是为了演示下对不。当执行这段代码后,我们会得到一个HelloImpl.class文件,其中的代码如下:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

import com.calm.proxy.HelloInterface;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class HelloImpl extends Proxy implements HelloInterface {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    public HelloImpl(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        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 void sayHello() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        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() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (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.calm.proxy.HelloInterface").getMethod("sayHello");
            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());
        }
    }
}

请记住这份代码不是.java文件而是.class文件,如果你不是用编译器打开的话你看到的会是看不懂的乱码,这里能看到是因为编译器为我们进行反解释,然后请注意下这个函数ProxyGenerator.generateProxyClass。好,我们继续看动态代理。

Hello hello = new Hello();
        HelloInterface helloInterface = (HelloInterface) Proxy.newProxyInstance(hello.getClass().getClassLoader(),
                hello.getClass().getInterfaces(),new ProxyHandler(hello));
        helloInterface.sayHello();
static class ProxyHandler implements InvocationHandler{
        private final Object object;
        public ProxyHandler(Object o){
            this.object = o;
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("Before invoke method");
            Object invoke = method.invoke(object, args);
            System.out.println("After invoke method");
            return invoke;
        }
    }

接口还是那个接口,委托类还是那个委托类。但代理类我不在直接写死,而是通过Proxy.newProxyInstance这个来生成。执行结果如下:

Before invoke method
Hello World!
After invoke method

执行结果与静态代理基本类似,但并没完全写死我的委托类,相对更加灵活。你是否用过大名鼎鼎的Retrofit,是不是感觉它做了好多事情,但实际真正做网络请求的是它么?我只能告诉你它真正是把动态代理玩到了极致。好了,以上的代码相信对于你来说肯定是小儿科的存在了,那么今天就到这里吧。想啥呢?只了解了基本的代码编写怎么行,知其然知其所以然方为知知。我们还是可以继续往下深入看下的嘛。
Proxy.newProxyInstance调用了Proxy的类函数,我们跟进去看下。

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

        /*
         * Look up or generate the designated proxy class.
         */
         //关键代码在这句,获取代理的类
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

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

这段代码会从proxyClassCache判断是新创建还是从缓存获取对应的类返回回去。创建的话是从下面这个类去进行的。

private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>

执行该类的apply方法。由于代码比较多就不全贴了,只贴出关键的代码。

/*
             * Generate the specified proxy class.
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            }

在apply的方法里我们会看到上面的代码。等等,ProxyGenerator.generateProxyClass这个熟悉不,还记得上面让注意的吗,是不是这个方法?对的,在这里也是通过这个方法来创建得到一个byte[],我们的.class不就是由.java编译而来的一份二进制文件,不就是一个byte[]么。其实如果你有兴趣可以自己将我们之前生成的HelloImpl.class通过类加载器加载进去,然后实例化并使用它。注意以上代码最后返回给我们的是Class<?>,是什么?这是一个已经被类加载器加载过的Class啦。我们回到newProxyInstance这个函数里可以看到如下代码:

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

到这里各位应该看明白了吧,得到Class然后反射创建对应的实例。到这里是不是可以收工了?to yong to simple,知道动态代理的类是怎么创建并实例化出来的,难度不看下又是怎么可以被正常调用的么?这时候我们之前的HelloImpl.class终于可以上场了,我们知道其实代理创建与我们自己通过函数创建的class是一样的,那我们调用的时候很明显也是调用了相同的代码,如下:

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

super.h,super是谁?h又是谁? 我们继承的是Proxy很明显这就是super,那h呢?

/**
     * the invocation handler for this proxy instance.
     * @serial
     */
    protected InvocationHandler h;

好吧,我们创建动态代理的时候不是传了个InvocationHandler 的实现类么,我想不用过多解释了吧。

m3 = Class.forName("com.calm.proxy.HelloInterface").getMethod("sayHello");

m3对应调用的方法,更具体的可以去看下对应的.class文件吧。
好了,到这里我们可以真正的手工了,从写法到对应的生成原理在到调用逻辑,这样的一套是不是可以让你更加印象深刻呢?感谢陪伴。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值