代理模式及其原理

代理,你可以想成显示生活中的代理,就是在被代理对象执行之前,代理进行相关工作的过程,Java中有静态代理和动态代理。

1、静态代理

静态代理比较简单,举例如下:

/**
* 被代理接口类
*/
public interface Rent {
    void rentHouse();
    String findHouse(String position);
}
/**
 * 被代理对象实现类
 */
public class RentInGZ implements Rent {

    @Override
    public void rentHouse() {
        System.out.println("rent this house, i am fulfilled");
    }

    @Override
    public String findHouse(String position) {
        System.out.println("the position is :" + position);
        return position;
    }

    public void time(int time){
        System.out.println("租期是:" + time + "days");
    }
}

/**
 * 静态代理
 */
public class StaticRentProxy implements Rent {

    private Rent rent;

    public StaticRentProxy(Rent rent) {
        this.rent = rent;
    }

    @Override
    public void rentHouse() {
        System.out.println("before rent house, i want to clean the house");
        rent.rentHouse();
    }

    @Override
    public String findHouse(String position) {
        return null;
    }
}

测试类如下:

public static void main(String[] args) {
        Rent rentInGZ = new RentInGZ();
        Rent rent = new StaticRentProxy(rentInGZ);
        rent.rentHouse();
    }

输出:
在这里插入图片描述
静态代理很好理解,其实就是把被代理类当成成员对象放到代理类里面,执行被代理类的方法的时候,因为代理类和被代理类实现的接口一致,所以也调用了和被代理对象一样的方法

但是这样子就有一些问题:
(1)可以看到我们的代理类RentHouseProxy和被代理类RentInGZ实现的要是同一个接口Rent,如果我们想要代理多个接口,并且这些接口的代理动作都是类似的(比如加相同模式的日志),实际上我们只需要写一个代理类就可以的,但是使用静态代理就要为每个接口写一个代理类,这样子就很麻烦
(2)同样也是因为代理类要实现接口Rent,如果接口Rent增加或者删除一些方法,那么代理类也要进行相应的操作,这其实是很难维护的

2、动态代理

基于上面这些问题,动态代理就出现了。动态代理怎么解决上面两个问题的呢,首先多个接口的问题,代理类内部成员对象是Obect类型,表示可以代理各种类型的对象,其次多方法问题,使用反射的方法,找到方法并传入参数进行执行

代理类实现接口InvocationHandler:

public class RentHouseHandler implements InvocationHandler {

    private Object object;

    public RentHouseHandler(Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before rent house");
        Object ret = method.invoke(object, args);
        return ret;
    }
}

测试的时候,怎么获取到代理类呢,如下:

public static void main(String[] args) {
        Rent rentInGZ = new RentInGZ();//1行
        RentHouseHandler rhHandler = new RentHouseHandler(rentInGZ);//2行
        Rent proxy = (Rent) Proxy.newProxyInstance(Rent.class.getClassLoader(), rentInGZ.getClass().getInterfaces(), rhHandler);//3行
        proxy.findHouse("Guangzhou");//4行
    }

执行之后结果如下:
在这里插入图片描述
这里使用的是Proxy.newProxyInstance()方法获取到代理对象,看到这里,你或许有下面疑问:
(1)Proxy.newProxyInstance是干什么的,三个参数代表什么意思
(2)为什么通过newProxyInstance()方法获取的对象可以直接转换为Rent类型
(3)代理类实现的InvocationHandler有什么用,invoke方法三个参数表示什么意思,为什么代理方法invoke()会被执行

估计一头雾水吧,哈哈,反正我当时看到这里是一脸懵逼~~~

3、动态代理实现原理

解答一下main()方法工作流程:第1、2行代码分别是生成被代理对象和InvocationHandler,第3行代码生成代理对象,这里生成的proxy实际上是com.sun.proxy.$Proxy类型,这个类是通过反射的方式动态生成的,它实现了Rent接口(后面解释),所以可以强制转换,另外$Proxy内部的方法调用了Proxy类的invoke方法,以达到调用InvocationHandler 接口类的invoke()方法,我们在RentHouseHandler 对InvocationHandler实现了,所以就调用了RentHouseHandler的invoke()方法,这样就达到了代理的目的

(1)newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)方法三个参数:
loader: the class loader to define the proxy class 为了实现我们代理类而需要的ClassLoader,这个参数目前还不知道有什么限制,我试验其实是可以传入Main.class.getClassLoader()、RentInGZ.class.getClassLoader()、Rent.class.getClassLoader(),甚至是可以其他和这里不相干的类的ClassLoader
interfaces: 被代理类实现的接口,这个很好理解啦,你要代理什么类,就传入这个类实现的接口喽
h:就是实现了InvocationHandler的代理handler了
这三个参数在后面生成$Proxy的时候会被使用到

(2)上面第二第三个问题的解答
在解答上面问题之前,我们先看一下Proxy.newProxyInstance()的源码:
newProxyInstance()方法里面生成实例的关键代码是:
设为代码段A

 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;
                    }
                });
            }
            //这里使用Constructor.newInstance(h)的方法生成代理类的实例,h是构造器的参数
            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);
        }
    }

关键步骤就是两个,首先getProxyClass0(loader, intfs);获取到Class对象,然后Class.getConstructor().newInstance()获取到实例,这样就返回了$Proxy实例

好,进入内部看看getProxyClass0(loader, intfs);方法:
设为代码段B

  private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
       //省略前面的判断
        return proxyClassCache.get(loader, interfaces);
    }

设为代码段C

 public V get(K key, P parameter) {
       ......
        while (true) {
            if (supplier != null) {
                // supplier might be a Factory or a CacheValue<V> instance
                //调用的factory.get()
                V value = supplier.get();
                if (value != null) {
                    return value;
                }
            }
            // else no supplier in cache
            // or a supplier that returned null (could be a cleared CacheValue
            // or a Factory that wasn't successful in installing the CacheValue)

            // lazily construct a Factory
            if (factory == null) {
                factory = new Factory(key, parameter, subKey, valuesMap);
            }

            if (supplier == null) {
                supplier = valuesMap.putIfAbsent(subKey, factory);
                if (supplier == null) {
                    // successfully installed Factory
                    supplier = factory;
                }
                // else retry with winning supplier
            } else {
                if (valuesMap.replace(subKey, supplier, factory)) {
                    // successfully replaced
                    // cleared CacheEntry / unsuccessful Factory
                    // with our Factory
                    supplier = factory;
                } else {
                    // retry with current supplier
                    supplier = valuesMap.get(subKey);
                }
            }
        }
    }

真正核心的代码是supplier.get(),supplier其实是被factory赋值的,上面这段代码给factory实例化:

 if (factory == null) {
                factory = new Factory(key, parameter, subKey, valuesMap);
            }

可以看到的是key就是ClassLoader,parameter就是interfaces,当调用supplier.get()的时候,实际上调用的是factory.get():
设为代码段D

 public synchronized V get() { // serialize access
          ......
            try {
                value = Objects.requireNonNull(valueFactory.apply(key, parameter));
            } finally {
                if (value == null) { // remove us on failure
                    valuesMap.remove(subKey, this);
                }
            }
           ......
            return value;
        
    }

这里调用的是Proxy.ProxyClassFactory.apply方法:
设为代码段E

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

            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            //(1)--------------------------------------------
            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());
                }
            }

//(2)----------------------------------------------------------------------

            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.
             * 生成 报名+类似$Proxy0 之类的名字
             */
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;

            /*
             * Generate the specified proxy class.
             * 这里生成字节码的$Proxy0
             */
            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());
            }
        }
    }

这个方法主要干了这2件事:(1)部分就是验证interfaces不要出现相同的类型导致冲突 (2)通过反射生成$Proxy

$Proxy这个类是动态生成的,可以通过保存到文件查看:

public static void main(String[] args) {
        Rent rentInGZ = new RentInGZ();
        RentHouseHandler rhProxy = new RentHouseHandler(rentInGZ);
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                rhProxy.getClass().getName(), rentInGZ.getClass().getInterfaces());
        try {
            FileOutputStream out = new FileOutputStream("proxy.class");
            out.write(proxyClassFile);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

然后反编译proxy.class文件,源代码如下:
设为代码段F

package pattern.com.helloAspect.self;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class RentHouseHandler extends Proxy implements Rent {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m4;
    private static Method m0;

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

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

    public final String findHouse(String var1) throws  {
        try {
            return (String)super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    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 void rentHouse() throws  {
        try {
            super.h.invoke(this, m4, (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)).intValue();
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
            m3 = Class.forName("pattern.com.helloAspect.self.Rent").getMethod("findHouse", new Class[]{Class.forName("java.lang.String")});
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            m4 = Class.forName("pattern.com.helloAspect.self.Rent").getMethod("rentHouse", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

这就是我们生成的$Proxy类了,可以看到这个类实现了Rent接口,我们测试类中的rent实例,是$Proxy()类型的,但是仍然可以强制转换为Rent

   public static void main(String[] args) {
        Rent rentInGZ = new RentInGZ();
        RentHouseHandler rhProxy = new RentHouseHandler(rentInGZ);
        Rent rent = (Rent) Proxy.newProxyInstance(rhProxy.getClass().getClassLoader(), new RentInGZ().getClass().getInterfaces(), rhProxy);
        System.out.println(rent.getClass());//输出  class com.sun.proxy.$Proxy0
    }

另外 我们可以看到,这个类的构造器是一个传入了InvocationHandler类型的参数的构造器,这就是为什么我们在Proxy.newInstanceProxy()传入InvocationHandler了,生成的源代码是对应“代码段A”的cons.newInstance(new Object[]{h});这一段代码,前面已经解释了

还有,这个类实现Rent接口的方法的时候,是直接通过h.invoke(this, m, new Object[]{var1})实现的,这样就可以解释为什么代理类执行被代理对象的方法的时候,会调用InvocationHanlder里面的invoke()方法了,理所当然的,我们也知道了InvocationHandler的invoke()方法的三个参数分别表示:$Proxy,被代理对象传入的方法,被代理对象传入方法的参数

结论:
(1)通过反编译$Proxy类我们可以知道,$Proxy继承了Proxy, 由于Java不能多继承,所以动态代理只支持借口的代理
(2)$Proxy里面有关equals、hansCode、toString方法也是调用的h.invoke(),所以也支持这三个方法的代理

结语

篇幅有点长,写的也不是很简洁,有什么写的不对的忘行内的大师多多指出,共同进步

参考:
https://blog.csdn.net/mhmyqn/article/details/48474815
https://www.jianshu.com/p/23d3f1a2b3c7

  • 10
    点赞
  • 65
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值