image.png
image.png

代理模式

理解:不直接访问目标类,通过代理类调用目标类来完成操作。

简而言之就是直接调用变为了间接调用。

好处:在代理类调用目标类之前和之后去添加一些预处理和后处理的操作,扩展不属于目标类的功能。

代理分类

静态代理:需要我们事先编写目标类对应的代理类代码,然后编译生成字节码文件。

在程序运行时直接去读这些字节码文件进行运行。

动态代理:不需要事先给目标类编写代理类代码,而是在运行中通过反射自动生成代理对象(以JDK动态代理举例)。

动态代理

JDK

JDK动态代理是基于Java的反射机制实现的。使用jdk中接口和类实现代理对象的动态创建。

JDK的动态要求目标对象必须实现接口,这是java设计上的要求。

从jdk1.3以来, java语言通过java.lang.reflect包提供三个类支持代理模式Proxy, Method和 InovcationHandler.

import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.List; public class TestReflect { public static void main(String[] args) { //System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles","true"); ProxyHandler proxyHandler = new ProxyHandler();
        List<String> list = (List)proxyHandler.getInstance(new ArrayList<String>());
        list.add("这里代理的是ArrayList类");
    }
} //实现InvocationHandler的Handler class ProxyHandler implements InvocationHandler{ private Object targetObj; @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("执行前");
        Object invoke = method.invoke(targetObj, args);
        System.out.println("执行后"); return invoke;
    } public Object getInstance(Object targetObj){ this.targetObj=targetObj; //创建一个代理类,参数固定: 目标对象类加载器,目标对象接口,实现了InvocationHandler的Handler return Proxy.newProxyInstance(
                targetObj.getClass().getClassLoader(),
                targetObj.getClass().getInterfaces(), this );
    }
}

由于ArrayList实现了List接口,我们直接拿ArrayList进行测试。

demo中并没有事先编写ArrayList的代理类,为什么能够代理ArrayList类呢?

  1. Proxy类中的newProxyInstance会调用getProxyClass0方法
  2. getProxyClass0会从proxyClassCache中获取Class<?>
  3. proxyClassCache构建的第二个参数 ProxyClassFactory()中apply()方***生成字节码
  4. ProxyGenerator.generateProxyClass()会根据saveGeneratedFiles这个boolean值判断是否在本地生成字节码文件
  5. 我们在测试代码中加入System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles","true")这句话就会在本地生成代理类的字节码文件。
public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h) throws IllegalArgumentException { /*
         * Look up or generate the designated proxy class.
         */ Class<?> cl = getProxyClass0(loader, intfs); //忽略其他代码 
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);
    }
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
private static final class ProxyClassFactory implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    { // 忽略其他代码 @Override public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) { /*
             * Generate the specified proxy class.
             */ byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
private static final boolean saveGeneratedFiles = (Boolean)AccessController.doPrivileged(new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles")); public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
        ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2); final byte[] var4 = var3.generateClassFile(); if (saveGeneratedFiles) { // 忽略其他代码 }
}
image.png
image.png
image.png
image.png

从图中可以看出,代理类对象实现了ArrayList类中实现的所有接口,并且继承了一个Proxy类。

Proxy类中有定义InvocationHandler 成员属性,

protected InvocationHandler h;

又因为代理类中有目标类中的所有方法,即可通过调用Proxy类的InvocationHandler的实现类的invoke方法来完成对目标类的代理。

jdk动态代理的缺陷:

  1. Java是单继承机制,目标类如果继承了一些父类的方法,那么代理类就无法操作那些方法;(因为代理类必须继承Proxy类,那就无法继承目标类的父类了)
  2. 目标类没有接口就无法实现动态代理,显然不合理。