首页 > 其他分享 >代理模式

代理模式

时间:2022-12-23 16:57:18浏览次数:56  
标签:Object 代理 模式 Class proxy new class

目录

一、简介

  为其它对象提供一种代理,以控制对这个对象的访问;代理对象就类似生活中的中介;归属于结构型设计模式

优点: 代理模式能将代理对象与真实被调用的目标对象分离;一定程度上降低了系统的耦合程度,易于扩展;代理可以起到保护目标对象的作用; 增强目标对象的职责

缺点: 代理模式会造成系统设计中类的数目增加;在客户端和目标对象之间增加了一个代理对象,会造成请求处 理速度变慢;增加了系统的复杂度

使用场景:不改变原有逻辑的前提下,批量地增加一些共同的逻辑

二、静态代理

显式声明被代理对象,仅可以代理某些对象

  • 代码实现
/**
 * 静态代理演示
 */
public class Test {

    // 代理类与被代理类均需实现该公共接口
    interface Ticket {

        // 卖票
        void sell();
    }

    // 演唱会门票——被代理类
    static class MusicTicket implements Ticket {
        @Override
        public void sell() {
            System.out.println("卖演唱会门票");
        }
    }

    // 代理类
    static class MusicConductor implements Ticket {

        private MusicTicket ticket;

        // 只代理 演唱会门票
        public MusicConductor(MusicTicket ticket) {
            this.ticket = ticket;
        }

        // 卖票
        @Override
        public void sell() {
            before();
            this.ticket.sell();
            after();
        }

        private void before() {
            System.out.println("静态代理 - 方法前增强");
        }

        private void after() {
            System.out.println("静态代理 - 方法后增强");
        }
    }

    // 测试
    public static void main(String[] args) {
        MusicTicket targetObj = new MusicTicket();
        targetObj.sell();
        // ###########################  [ 输出 ]  ###########################
        // 卖演唱会门票
        System.out.println("############  [ 分割线 ]  ############");
        Ticket proxy = new MusicConductor(targetObj);
        proxy.sell();
        // ###########################  [ 输出 ]  ###########################
        // 静态代理 - 方法前增强
        // 卖演唱会门票
        // 静态代理 - 方法后增强
    }
}
  • UML图例
    image

三、动态代理(重要)

动态的配置和替换被代理对象,通俗的说就是可以代理任意一类对象,甚至是任意对象

3.1、JDK 动态代理

注意:JDK动态代理只能代理含有有接口的类,并且只能代理接口方法

  是因为JDK动态代理本身机制决定的,在java里面动态代理是Proxy.newProxyInstance()这个方法来实现的,它需要传入被动态代理的一个接口类,还是取决于JDK动态代理的的一个底层实现,JDK动态代理会在程序运行期间,动态的生产一个代理类叫$Proxy0,这个代理类会继承java.lang.reflect.Proxy类,同时会实现被代理类的接口,在java里面不支持类的多继承,而每个动态代理都继承一个Proxy,所以就导致的JDK里面的动态代理只能代理接口,而不能代理实现类

  • 使用场景

  动态代理本身的使用场景或者需求,是对原始目标类的某个方法实现拦截,然后对目标方法进行增强或,在实际开发过程中,大多是基于面向接口进行开发,所以基于接口来实现动态代理从需求和场景都是吻合的。当然确实会存在没有实现接口的类,这种情况要去实现动态代理,JDK 显然无法满足

  在 java 里面类的设计,更多的是考虑到共性能力的抽象,从而提高代码的重用性和扩展性,而动态代理其实也在做这样的事情,它封装了动态代理的生成和抽象逻辑,以及判断一个类是否是动态代理类,以及InvocationHandler的一个持有等等,那么这个抽象逻辑放在Proxy这个父类里面,很显然也是一个正常的一个思路

  • 代码实现
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;

/**
 * JDK动态代理演示
 */
public class Test {

    // 接口
    interface Ticket {

        // 卖票
        void sell();
    }

    // 被代理类1
    static class MusicTicket implements Ticket {
        @Override
        public void sell() {
            System.out.println("卖演唱会门票");
        }
    }

    // 被代理类2
    static class SportTicket implements Ticket {
        @Override
        public void sell() {
            System.out.println("体育比赛门票");
        }
    }

    // 代理类(必须实现 JDK 提供的 InvocationHandler 接口)
    static class Conductor implements InvocationHandler {
        // 被代理对象
        private Object target;

        /**
         * 可以代理任意门票,所以为 Object
         *
         * @param target 被代理对象,但是必须有统一的接口
         * @return 被代理对象
         */
        public Object getInstance(Object target) {
            this.target = target;
            Class<?> clazz = target.getClass();
            return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
        }

        /**
         * 反射调用的方法
         *
         * @param proxy  代理对象
         * @param method 被代理对象需要执行的方法
         * @param args   被代理对象需要执行的方法 的参数
         * @return 被代理对象需要执行的方法 的返回值
         * @throws Throwable 抛出异常
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            before();
            Object result = method.invoke(target, args);
            after();
            return result;
        }

        private void before() {
            System.out.println("动态代理 - JDK动态代理 - 方法前增强");
        }

        private void after() {
            System.out.println("动态代理 - JDK动态代理 - 方法后增强");
        }

        // 测试
        public static void main(String[] args) {
            List a = new ArrayList();
            a.add(123);
            Ticket b = new MusicTicket();

            // 代理演唱会门票
            Ticket t1 = (Ticket) new Conductor().getInstance(new MusicTicket());
            t1.sell();
            System.out.println("###############  \t[ 分割线 ]\t  ###############");
            // 代理运动会门票
            Ticket t2 = (Ticket) new Conductor().getInstance(new SportTicket());
            t2.sell();
        }
    }
}
  • UML图例

image

3.2、JDK 动态代理的原理分析

  • 原理步骤

1、拿到被代理的对象的引用,反射获取所有接口

2、JDK 的 Proxy 类重新生成一个新的类,实现了被代理对象的所有接口

3、Proxy 根据字节码动态生成 Java 代码,把增强的逻辑加入到新生成的代码中

4、编译生成的 Java 代码对应的 class 文件

5、通过 ClassLoader 加载并重新运行新的 class 文件(注意这个 class 文件就是一个全新的类)

  • JDK1.8源码解读
// 1. musicTicket 是通过我们定义的 getInstance 方法返回的,在 getInstance 方法里,我们通过调用 Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this)

// 2. 我们看看 java.lang.reflect.Proxy#newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 
// java.lang.reflect.Proxy#newProxyInstance 主要工作就是①生成代理类②根据代理类获取构造方法③通过构造方法生成代理对象
// ①生成代理类 -> Class<?> cl = getProxyClass0(loader, intfs);
// ②根据代理类获取构造方法 -> final Constructor<?> cons = cl.getConstructor(constructorParams);
// ③通过构造方法生成代理对象 -> return cons.newInstance(new Object[]{h});

// 3. 这里的重点在于生成代理类,所以我们看看 getProxyClass0 这个方法 -> java.lang.reflect.Proxy#getProxyClass0(ClassLoader loader,Class<?>... interfaces)

/**
 * Generate a proxy class.  Must call the checkProxyAccess method
 * to perform permission checks before calling this.
 * 生成代理类,调用该方法前,必须先执行checkProxyAccess方法校验是否可以生成代理类。
 */
private static Class<?> getProxyClass0(ClassLoader loader,Class<?>... interfaces) {
    // 这里需要注意:接口的数量有最大限制,不可以超过 65535
    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
    // 如果缓存中存在代理类,则直接返回,如果不存在,则会调用ProxyClassFactory类生成代理类
    return proxyClassCache.get(loader, interfaces);
}

// 4. 我们首次调用的时候,缓存里肯定是不存在的,所以我们先看看这个 proxyClassCache 到底是何方神圣?
private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

// 4.1. 这里的 WeakCache 是JDK自己通过 ConcurrentMap 实现的一个缓存
// 4.2. 键:KeyFactory 就是 ClassLoader 类加载器
// 4.3. 值:ProxyClassFactory 就是代理类的工厂类对象

//5. 我们查看下 java.lang.reflect.WeakCache#get(K key, P parameter) 这个方法里到底是怎么处理的
/**
 * @param key 类加载器
 * @param parameter 接口数组
 */
public V get(K key, P parameter) {
    // 校验接口数组,必须存在接口,否则直接抛出 NPE
    Objects.requireNonNull(parameter);

    // 清除已经被GC回收的对象引用
    expungeStaleEntries();

    // 将 ClassLoader 转化为 CacheKey,此时cacheKey为一级缓存的key
    Object cacheKey = WeakCache.CacheKey.valueOf(key, refQueue);

    // lazily install the 2nd level valuesMap for the particular cacheKey
    // 根据一级缓存的key获取二级缓存
    ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
    // 二级缓存不存在,创建一个空的二级缓存
    if (valuesMap == null) {
        ConcurrentMap<Object, Supplier<V>> oldValuesMap
                = map.putIfAbsent(cacheKey,
                valuesMap = new ConcurrentHashMap<>());
        if (oldValuesMap != null) {
            valuesMap = oldValuesMap;
        }
    }

    // create subKey and retrieve the possible Supplier<V> stored by that
    // subKey from valuesMap
    // 根据 ClassLoader 和 接口数组,创建二级缓存的key
    Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
    // 通过二级缓存的key获取值
    Supplier<V> supplier = valuesMap.get(subKey);
    WeakCache.Factory factory = null;

    // 死循环重试,直到返回对象为止
    while (true) {
        // 程序的出口分支
        if (supplier != null) {
            // supplier might be a Factory or a CacheValue<V> instance
            // 根据下方的方法可以看出 supplier 可能是 WeakCache.Factory 也可能是 CacheValue
            // 具体验证的细节,在 supplier 的实现类里去判断是什么类型,然后将值返回
            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
        // 如果二级缓存的key取不到值,并且Factory不存在,则去创建Factory对象,这一条件分支,若执行,仅会执行一次
        // 在下方代码可见,创建的Factory对象会去充当二级缓存的值
        if (factory == null) {
            factory = new WeakCache.Factory(key, parameter, subKey, valuesMap);
        }

        if (supplier == null) {
            // 如果二级缓存的key取不到值, 就将factory作为二级缓存对应的值放入
            // 防止被其他线程修改,所以使用 putIfAbsent 方法是如果存在 subKey,就取出来直接用,如果不存在,则将 factory 放入
            supplier = valuesMap.putIfAbsent(subKey, factory);
            // 存放失败的情况,强制将 supplier = factory,此时factory成功加入二级缓存
            if (supplier == null) {
                // successfully installed Factory
                supplier = factory;
            }
            // else retry with winning supplier
        } else {
            // 如果被其他线程修改,就尝试将factory替换旧值
            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);
            }
        }
    }
}

// 6. 根据如上代码,这里关键就2点:①二级缓存key的创建②二级缓存只的获取
// ①二级缓存key的创建 -> Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
// ②二级缓存值的获取 -> V value = supplier.get();

// 7. 首先我们来看看二级缓存key的创建,这里通过 subKeyFactory.apply() 获取,那么这个 subKeyFactory 是什么呢?我们可以跟踪源码发现,这是通过 WeakCache 的构造方法传入的参数,也就是说,是在 Proxy 类里创建 proxyClassCache 的是传入的,所以这个 subKeyFactory 就是 java.lang.reflect.Proxy.KeyFactory 类的对象

/**
 * A function that maps an array of interfaces to an optimal key where
 * Class objects representing interfaces are weakly referenced.
 * 将接口数组映射为一个最优的键去代表接口的实现类的弱引用的方法
 */
private static final class KeyFactory
        implements BiFunction<ClassLoader, Class<?>[], Object>
{
    @Override
    public Object apply(ClassLoader classLoader, Class<?>[] interfaces) {
        switch (interfaces.length) {
            // 这里的 Proxy.key1....这些就是根据哈希值计算key
            case 1: return new Proxy.Key1(interfaces[0]); // the most frequent
            case 2: return new Proxy.Key2(interfaces[0], interfaces[1]);
            case 0: return key0;
            default: return new Proxy.KeyX(interfaces);
        }
    }
}

// 8. 然后我们到了关键的地方,敲黑板,划重点了!!!我们来看下二级缓存值的获取,在WeakCache中是通过 supplier.get() 获取的,那么关键就是这个 supplier 了。
// 还记得上面说过的,V value = supplier.get() 取到的可能是 WeakCache.Factory 也可能是 CacheValue
// 那我们看看 WeakCache.Factory 

/**
 * A factory {@link Supplier} that implements the lazy synchronized
 * construction of the value and installment of it into the cache.
 * 将值设置进缓存的工厂类,线程安全
 */
private final class Factory implements Supplier<V> {

    private final K key;
    private final P parameter;
    private final Object subKey;
    private final ConcurrentMap<Object, Supplier<V>> valuesMap;

    Factory(K key, P parameter, Object subKey, ConcurrentMap<Object, Supplier<V>> valuesMap) {
        this.key = key;
        this.parameter = parameter;
        this.subKey = subKey;
        this.valuesMap = valuesMap;
    }

    @Override
    public synchronized V get() { // serialize access
        // re-check
        // 双重检查,缓存中是否已存在二级缓存key
        Supplier<V> supplier = valuesMap.get(subKey);
        // 之所以之前不去校验,是因为这里会去判断,是否是 WeakCache.Factory 对象还是 CacheValue
        // 有可能被替换成了 CacheValue 或者被移除了
        // 总之如果是不是 Factory 对象本身,就返回 null,继续之前的死循环
        if (supplier != this) {
            // something changed while we were waiting:
            // might be that we were replaced by a CacheValue
            // or were removed because of failure ->
            // return null to signal WeakCache.get() to retry
            // the loop

            return null;
        }
        // else still us (supplier == this)

        // create new value
        V value = null;
        try {
            // 这里调用 valueFactory.apply(key, parameter) 获取值
            value = Objects.requireNonNull(valueFactory.apply(key, parameter));
        } finally {
            if (value == null) { // remove us on failure
                valuesMap.remove(subKey, this);
            }
        }
        // the only path to reach here is with non-null value
        assert value != null;

        // wrap value with CacheValue (WeakReference)
        // 将返回的对象,包装成 CacheValue(弱引用)
        WeakCache.CacheValue<V> cacheValue = new WeakCache.CacheValue<>(value);

        // put into reverseMap
        reverseMap.put(cacheValue, Boolean.TRUE);

        // try replacing us with CacheValue (this should always succeed)
        if (!valuesMap.replace(subKey, this, cacheValue)) {
            throw new AssertionError("Should not reach here");
        }

        // successfully replaced us with new CacheValue -> return the value
        // wrapped by it
        return value;
    }
}

// 观察 java.lang.reflect.WeakCache.Factory 这个类的 get 方法可以发现,这里解决前面没有去判断返回值类型的坑
// 如果不是 Factory 对象本身,就返回 null,继续之前的死循环
// 如果是 Factory 对象,就通过 value = Objects.requireNonNull(valueFactory.apply(key, parameter)) 获取值
// 所以这个 valueFactory 就是关键的地方,有了上面 subKeyFactory 的经验,我们可以很快知道这个 valueFactory 也是在构造 WeakCache 传进来的,也就是说,是在 Proxy 类里创建 proxyClassCache 的是传入的,所以这个 valueFactory 就是 java.lang.reflect.Proxy.ProxyClassFactory 类的对象

// 9. 接下来就需要来拜读下这个 java.lang.reflect.Proxy.ProxyClassFactory 这个类的源码了

/**
 * A factory function that generates, defines and returns the proxy class given
 * the ClassLoader and array of interfaces.
 * 根据 ClassLoader 和 接口数组生成、定义和返回给定的代理类的工厂函数
 */
private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
    // prefix for all proxy class names
    // 定义了所有代理类的名称前缀为「$Proxy」
    private static final String proxyClassNamePrefix = "$Proxy";

    // next number to use for generation of unique proxy class names
    // 为代理类对象生成编号,从0递增,如 $Proxy0、$Proxy1 等
    // 使用 AtomicLong 原子类保证线程安全
    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.
             * 检查指定的ClassLoader加载接口得到的Class对象是否和接口数组遍历得到的Class对象相同
             */
            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.
             * 检查当前 Class 对象不是一个接口
             */
            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.
         * 记录所有非public的代理接口的包路径,验证是否代理接口再同一个包下。
         * 公共接口无需处理,只验证非public接口
         */
        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");
                }
            }
        }

        // 如果都是 public 修饰的接口,那么就将代理类的包名设置为 ReflectUtil.PROXY_PACKAGE -> com.sun.proxy
        // 注意:万一出现 java.io.FileNotFoundException: com\sun\proxy\$Proxy0.class 这个异常,可以通过在项目路径手动创建 com.sun.proxy 包解决
        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();

        // 代理类名称,类似:com.sun.proxy.$Proxy0.class
        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());
        }
    }
}

// 好了,重头戏开始了,生成代理类文件的具体代码就要出现了
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);

// 10. 我们来看看 sun.misc.ProxyGenerator#generateProxyClass(final String var0, Class<?>[] var1, int var2)这个方法到底做了什么神奇的事情
public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
    ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
    // 这里才真正调用了生成类字节码的方法
    final byte[] var4 = var3.generateClassFile();
    // 如果需要将生成的字节码保存下来的话,就需要在这里设置 saveGeneratedFiles -> true
    if (saveGeneratedFiles) {
        AccessController.doPrivileged(new PrivilegedAction<Void>() {
            public Void run() {
                try {
                    int var1 = var0.lastIndexOf(46);
                    Path var2;
                    if (var1 > 0) {
                        Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
                        Files.createDirectories(var3);
                        var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
                    } else {
                        var2 = Paths.get(var0 + ".class");
                    }

                    Files.write(var2, var4, new OpenOption[0]);
                    return null;
                } catch (IOException var4x) {
                    throw new InternalError("I/O exception saving generated file: " + var4x);
                }
            }
        });
    }

    return var4;
}

// 11. 得,生成字节码的代码还藏了一层!不过也算是终于见到庐山真面目了,同时我们还发现了,我们也可以通过设置参数saveGeneratedFiles将生成的代理类保存下来。
// 12. 最后看看生成字节码的方法吧,sun.misc.ProxyGenerator#generateClassFile
private byte[] generateClassFile() {
    // 添加 hashCode 代理方法
    this.addProxyMethod(hashCodeMethod, Object.class);
    // 添加 equals 代理方法
    this.addProxyMethod(equalsMethod, Object.class);
    // 添加 toString 代理方法
    this.addProxyMethod(toStringMethod, Object.class);
    Class[] var1 = this.interfaces;
    int var2 = var1.length;

    int var3;
    Class var4;
    // 遍历所有接口中的所有方法,并将所有方法添加代理方法
    for(var3 = 0; var3 < var2; ++var3) {
        var4 = var1[var3];
        Method[] var5 = var4.getMethods();
        int var6 = var5.length;

        for(int var7 = 0; var7 < var6; ++var7) {
            Method var8 = var5[var7];
            this.addProxyMethod(var8, var4);
        }
    }

    Iterator var11 = this.proxyMethods.values().iterator();

    List var12;
    // 校验方法返回值
    while(var11.hasNext()) {
        var12 = (List)var11.next();
        checkReturnTypes(var12);
    }

    Iterator var15;
    try {
        // 添加代理类的构造方法
        this.methods.add(this.generateConstructor());
        var11 = this.proxyMethods.values().iterator();

        while(var11.hasNext()) {
            var12 = (List)var11.next();
            var15 = var12.iterator();

            while(var15.hasNext()) {
                ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();
                this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10));
                // 生成代理类的代理方法
                this.methods.add(var16.generateMethod());
            }
        }

        // 生成静态代码块等初始化信息
        this.methods.add(this.generateStaticInitializer());
    } catch (IOException var10) {
        throw new InternalError("unexpected I/O Exception", var10);
    }

    // 代理类的方法个数不可以超过 65535 个
    if (this.methods.size() > 65535) {
        throw new IllegalArgumentException("method limit exceeded");
    }
    // 代理类的字段个数不可以超过 65535 个
    else if (this.fields.size() > 65535) {
        throw new IllegalArgumentException("field limit exceeded");
    }
    // 都满足要求,开始写出字节码文件
    else {
        // 将类名、java/lang/reflect/Proxy、以及接口数组等信息保存进 cp 中,cp是一个常量池
        this.cp.getClass(dotToSlash(this.className));
        this.cp.getClass("java/lang/reflect/Proxy");
        var1 = this.interfaces;
        var2 = var1.length;

        for(var3 = 0; var3 < var2; ++var3) {
            var4 = var1[var3];
            this.cp.getClass(dotToSlash(var4.getName()));
        }

        // 设置常量池为只读,生成字节码文件之前,不可以进行修改
        this.cp.setReadOnly();
        ByteArrayOutputStream var13 = new ByteArrayOutputStream();
        DataOutputStream var14 = new DataOutputStream(var13);

        // 完犊子,终于开始写出字节码文件了
        try {
            var14.writeInt(-889275714);
            var14.writeShort(0);
            var14.writeShort(49);
            this.cp.write(var14);
            var14.writeShort(this.accessFlags);
            var14.writeShort(this.cp.getClass(dotToSlash(this.className)));
            var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
            var14.writeShort(this.interfaces.length);
            Class[] var17 = this.interfaces;
            int var18 = var17.length;

            for(int var19 = 0; var19 < var18; ++var19) {
                Class var22 = var17[var19];
                var14.writeShort(this.cp.getClass(dotToSlash(var22.getName())));
            }

            var14.writeShort(this.fields.size());
            var15 = this.fields.iterator();

            while(var15.hasNext()) {
                ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next();
                var20.write(var14);
            }

            var14.writeShort(this.methods.size());
            var15 = this.methods.iterator();

            while(var15.hasNext()) {
                ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next();
                var21.write(var14);
            }

            var14.writeShort(0);
            return var13.toByteArray();
        } catch (IOException var9) {
            throw new InternalError("unexpected I/O Exception", var9);
        }
    }
}

// 13. 关于其中的addProxyMethod这个方法,这里我就不导读源码了,有需要的话大家自己去看看,基本就是通过反射去取关键信息
// 14. generateConstructor这个方法中,我们可以看出来,在生成代理类的构造方法的时候,参数是 InvocationHandler
// 15. 所以说,在JDK动态代理中,InvocationHandler 是核心,调用时真正走的是 InvocationHandler 的 invoke() 方法 -> java.lang.reflect.InvocationHandler#invoke(Object proxy, Method method, Object[] args) 根据传入的代理对象、方法、和参数决定具体调用的代理方法。

// 16. 大家可以通过上方设置参数saveGeneratedFiles将生成的代理类保存下来,然后反编译看看JDK动态代理生成的代码到底是什么样的
// 17. 好了,到这儿,JDK动态代理的源码分析就结束啦

3.3、CGLIB 动态代理

  CGLIB 是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。

注意:CGLIB 不能代理被 final关键字修饰的类 / 方法

image

推荐使用spring框架带的核心
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>5.3.8</version>
</dependency>

<!--<dependency>-->
<!--    <groupId>cglib</groupId>-->
<!--    <artifactId>cglib</artifactId>-->
<!--    <version>3.3.0</version>-->
<!--</dependency>-->
  • 代码实现
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * CGLIB 动态代理演示
 */
public class Test {

    // 代理类1
    static class TrainTicket {
        public void sellTrainTicket(Integer num) {
            System.out.println("卖了[ " + num + " ]张火车票");
        }
    }

    // 代理类2
    static class MusicTicket {
        public void sellMusicTicket(Integer count) {
            System.out.println("卖了[ " + count + " ]张演唱会门票");
        }
    }

    // 代理类
    static class Conductor implements MethodInterceptor {
        // 生成clazz类的子类对象
        public Object getInstance(Class<?> clazz) {
            // Enhancer 相当于 JDK 动态代理的 Proxy 类
            Enhancer enhancer = new Enhancer();
            // 设置动态生成的对象的父类为传进来的 被代理类
            enhancer.setSuperclass(clazz);
            // MethodInterceptor 继承 Callback 接口
            enhancer.setCallback(this);
            return enhancer.create();
        }

        /**
         * 代理对象执行的所有方法都会走这个方法
         *
         * @param target           被代理的对象
         * @param targetMethod     被代理对象需要执行的方法
         * @param targetMethodArgs 被代理对象需要执行的方法 参数
         * @param methodProxy      父类代理方法
         * @return 被代理对象需要执行的方法 返回值
         * @throws Throwable 抛出的异常信息
         */
        @Override
        public Object intercept(Object target, Method targetMethod, Object[] targetMethodArgs, MethodProxy methodProxy) throws Throwable {
            before();
            // 调用生成代理对象的父类方法
            Object result = methodProxy.invokeSuper(target, targetMethodArgs);
            after();
            return result;
        }

        private void before() {
            System.out.println("动态代理 - CGLIB动态代理 - 方法前增强");
        }

        private void after() {
            System.out.println("动态代理 - CGLIB动态代理 - 方法后增强");
        }
    }

    public static void main(String[] args) {
        // 代理火车票
        TrainTicket t1 = (TrainTicket) new Conductor().getInstance(TrainTicket.class);
        t1.sellTrainTicket(1);

        // 代理演唱会门票
        MusicTicket t2 = (MusicTicket) new Conductor().getInstance(MusicTicket.class);
        t2.sellMusicTicket(10);
    }
}
  • 实际应用

Spring AOP

3.4、CGLIB 实现原理

  • 原理步骤

1、拿到被代理的类

2、为 CGLIB 的 Enhancer 设置父类,及其需要回调的类

3、org.springframework.cglib.proxy.Enhancer 调用 create() 方法生成被代理对象,被代理对象会继承原先的类

4、重写被代理的方法,同时加上 final 修饰符,不可再被重写,同时将增强的逻辑加入到代理方法前后

  • 源码分析
// trainTicket 是通过 getInstance() 获取的
// 1. 我们先看看 getInstance 主要做了什么操作
public Object getInstance(Class<?> clazz) {
    // Enhancer 相当于 JDK 动态代理的 Proxy 类
    Enhancer enhancer = new Enhancer();
    // 设置动态生成的对象的父类为传进来的 被代理类
    enhancer.setSuperclass(clazz);
    // MethodInterceptor 继承 Callback 接口
    enhancer.setCallback(this);
    return enhancer.create();
}

// 2. 调用的是 org.springframework.cglib.proxy.Enhancer#create() 来创建被代理类的子类对象
/**
 * Generate a new class if necessary and uses the specified
 * callbacks (if any) to create a new object instance.
 * Uses the no-arg constructor of the superclass.
 * 如果有必要的话,就创建一个新的类,并根据指定的回调创建一个对象。
 * 使用父类的无参构造方法创建对象
 * @return a new instance
 */
public Object create() {
    classOnly = false;
    argumentTypes = null;
    return createHelper();
}

// 3. 发现 create() 方法,调用的是 createHelper() 方法 -> net.sf.cglib.proxy.Enhancer#createHelper()
private Object createHelper() {
    // 判断 callbackTypes、filter 是否为空,同时对为空的条件做预处理
    preValidate();
    // 创建一个 Key
    Object key = KEY_FACTORY.newInstance((superclass != null) ? superclass.getName() : null,
            ReflectUtils.getNames(interfaces),
            filter == ALL_ZERO ? null : new WeakCacheKey<CallbackFilter>(filter),
            callbackTypes,
            useFactory,
            interceptDuringConstruction,
            serialVersionUID);
    this.currentKey = key;
    // 调用父类 -> AbstractClassGenerator 的 create 方法
    Object result = super.create(key);
    return result;
}

// 4. 具体生成的逻辑走的是 net.sf.cglib.core.AbstractClassGenerator#create(Object key) 方法
protected Object create(Object key) {
    try {
        // 获取 ClassLoader
        ClassLoader loader = getClassLoader();
        Map<ClassLoader, AbstractClassGenerator.ClassLoaderData> cache = CACHE;
        AbstractClassGenerator.ClassLoaderData data = cache.get(loader);
        if (data == null) {
            // 保证线程安全
            synchronized (AbstractClassGenerator.class) {
                cache = CACHE;
                data = cache.get(loader);
                // 双重检查,确保线程安全
                if (data == null) {
                    Map<ClassLoader, AbstractClassGenerator.ClassLoaderData> newCache = new WeakHashMap<ClassLoader, AbstractClassGenerator.ClassLoaderData>(cache);
                    data = new AbstractClassGenerator.ClassLoaderData(loader);
                    newCache.put(loader, data);
                    CACHE = newCache;
                }
            }
        }
        this.key = key;
        Object obj = data.get(this, getUseCache());
        if (obj instanceof Class) {
            return firstInstance((Class) obj);
        }
        // 具体创建对象的方法
        return nextInstance(obj);
    } catch (RuntimeException e) {
        throw e;
    } catch (Error e) {
        throw e;
    } catch (Exception e) {
        throw new CodeGenerationException(e);
    }
}

// 5. 具体创建对象的方法是 nextInstance(),这个方法是个抽象方法,具体实现由子类 Enhancer 实现
abstract protected Object nextInstance(Object instance) throws Exception;

// 6. 我们现在看看 net.sf.cglib.proxy.Enhancer#nextInstance(Object instance) 方法
protected Object nextInstance(Object instance) {
    Enhancer.EnhancerFactoryData data = (Enhancer.EnhancerFactoryData) instance;

    if (classOnly) {
        return data.generatedClass;
    }

    Class[] argumentTypes = this.argumentTypes;
    Object[] arguments = this.arguments;
    if (argumentTypes == null) {
        argumentTypes = Constants.EMPTY_CLASS_ARRAY;
        arguments = null;
    }
    return data.newInstance(argumentTypes, arguments, callbacks);
}
// 通过 data.newInstance(argumentTypes, arguments, callbacks) 返回具体对象,第一个参数为代理对象的构成器类型,第二个为代理对象构造方法参数,第三个为对应回调对象

/**
 * Creates proxy instance for given argument types, and assigns the callbacks.
 * Ideally, for each proxy class, just one set of argument types should be used,
 * otherwise it would have to spend time on constructor lookup.
 * 通过给定的参数类型,创建代理对象,同时分配回调。
 * 理想情况下,每一个代理类,应该仅使用一组构造参数,否则,我们会花费很多的时间去找到对应的构造方法
 * Technically, it is a re-implementation of {@link Enhancer#createUsingReflection(Class)},
 * with "cache {@link #setThreadCallbacks} and {@link #primaryConstructor}"
 *
 * @see #createUsingReflection(Class)
 * @param argumentTypes constructor argument types
 * @param arguments constructor arguments
 * @param callbacks callbacks to set for the new instance
 * @return newly created proxy
 */
public Object newInstance(Class[] argumentTypes, Object[] arguments, Callback[] callbacks) {
    setThreadCallbacks(callbacks);
    try {
        // Explicit reference equality is added here just in case Arrays.equals does not have one
        if (primaryConstructorArgTypes == argumentTypes ||
                Arrays.equals(primaryConstructorArgTypes, argumentTypes)) {
            // If we have relevant Constructor instance at hand, just call it
            // This skips "get constructors" machinery
            return ReflectUtils.newInstance(primaryConstructor, arguments);
        }
        // Take a slow path if observing unexpected argument types
        return ReflectUtils.newInstance(generatedClass, argumentTypes, arguments);
    } finally {
        // clear thread callbacks to allow them to be gc'd
        setThreadCallbacks(null);
    }

}

// 到这里就生成了具体的对象
// 如果需要看到具体生成的字节码对象是啥,可以在调用 enhancer.create() 方法前,调用如下方法,可以在对应位置生成class文件,然后通过 jd-gui 这种反编译工具,就可以查看到具体代码啦~
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "路径");

3.5、JDK 动态代理与 CGLIB 动态代理的区别

  • JDK 动态代理生成的代理对象是实现了被代理对象的接口,CGLIB 动态代理生成的代理对象是继承了被代理对象。
  • JDK 和 CGLIB 都是在运行期生成字节码,JDK 是直接写 class 字节码,CGLIB 使用 ASM 框架写 class 字节码,CGLIB 代理实现更复杂,CGLIB 生成代理类的效率比 JDK 生成代理类效率低
  • JDK 调用代理方法,是通过反射机制调用,CGLIB 是通过 FastClass 机制直接调用方法,CGLIB 的被代理类执行效率比 JDK 的被代理类更高

3.6、Spring 中动态代理

  • 当 Bean 有实现接口时,Spring 就会用 JDK 的动态代理。
  • 当 Bean 没有实现接口时,Spring 选择 CGLib。
  • Spring 可以通过配置强制使用 CGLib

其它

原文地址:https://xkcoding.com/2019/08/20/design-pattern-proxy.html

标签:Object,代理,模式,Class,proxy,new,class
From: https://www.cnblogs.com/hhddd-1024/p/17001049.html

相关文章

  • 外观模式
    软件设计                 石家庄铁道大学信息学院 实验12:外观模式本次实验属于模仿型实验,通过本次实验学生将掌握以下内容:1、理解外观模式的动机......
  • HIS互联网医院门诊信息管理系统源码JAVA语言开发 现场挂号 Spring + SpringBoot B/S模
    项目技术:HIS功能介绍:前台:挂号、患者基本信息、姓名、年龄、性别、身份证号、出生日期、住址挂号信息:挂号科室、号别、门诊医生、费用、(支付宝、微信)、现场挂号、退号......
  • 设计模式之美--结构型设计模式的区别
    结构型设计模式:主要总结一些类和对象组合在一起的经典结构,这些经典结构去解决特定应用场景的问题。代理模式:用来给原始类附加不想关的其他功能;装饰器模式:适配器模式:桥......
  • WorkPlus助力中交四航局打造数字化管理新模式,释放企业生产力
    企业简介 中交四航局正式创立于1951年,集团主要从事海内外港口、公路、桥梁、铁路、市政工程、水利工程等大型基础设施建设,以及相关的投资、勘察设计、科研、工业造船和......
  • 设计模式之美--原型模式
    原型模式:利用对原有对象进行复制拷贝的方式创建对象;浅拷贝只拷贝引用对象的引用地址,不拷贝引用对象本身;实际是操作同一个对象;例如:map的clone方法;深拷贝复制出一个新的......
  • 高性能web服务器nginx和反向代理
    高性能web服务器——nginx一、 简介1. nginx是什么?l 是一个使用c语言开发的高性能的http服务器和反向代理服务器以及电子邮件(IMAP/POP3)代理服务器。l 是俄罗斯的程序设......
  • 爬虫代理IP设置
      代理网站:​​http://www.goubanjia.com/​​  浏览器访问下试试: ......
  • WorkPlus助力中交四航局打造数字化管理新模式,释放企业生产力
    企业简介:中交四航局正式创立于1951年,集团主要从事海内外港口、公路、桥梁、铁路、市政工程、水利工程等大型基础设施建设,以及相关的投资、勘察设计、科研、工业造船和房地产......
  • 设计模式之美--建造者模式和工厂模式的区别
    工厂模式用来创建类型不同但相关的对象(继承同一父类或接口的一组子类),有给定的参数来决定创建哪种类型的对象;建造者模式用来创建同一种类型的复杂对象,通过设置不同的可选......
  • 百度工程师教你玩转设计模式(装饰器模式)
    作者|北极星小组想要写好代码,设计模式(DesignPattern)是必不可少的基本功,设计模式是对面向对象设计(ObjectOrientedDesign)中反复出现的一类问题的一种解决方案,本篇介绍装......