摘要

相比于静态代理,动态代理避免了开发人员编写各个繁锁的静态代理类,只需简单地指定一组接口及目标类对象就能动态的获得代理对象。

这里说的静态代理可以理解为之前使用的装饰者模式,从之前使用装饰者模式实现自己实现一个数据库连接池就可以明显可以看出它的缺点,如果接口有很多方法,而我们仅要装饰使用其中部分方法,我们还是不可避免的要实现它的其它方法。而动态代理就可以帮我们更细粒度的仅对我们要使用的方法进行增强。

JDK动态代理

使用

1、定义一个接口:

package com.zze.service;

public interface IWaiter {
    void service();
}

com.zze.service.IWaiter

2、定义它的实现类:

package com.zze.service.impl;

import com.zze.service.IWaiter;

public class Waiter implements IWaiter {
    public void service(){
        System.out.println("正在服务");
    }
}

com.zze.service.impl.Waiter

3、使用 JDK 提供的动态代理:

@Test
public void test() {
    IWaiter waiter = new Waiter();
    Class<?>[] interfaces = Waiter.class.getInterfaces();
    IWaiter waiterProxy = (IWaiter) Proxy.newProxyInstance(Waiter.class.getClassLoader(), interfaces, new InvocationHandler() {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object obj = null;
            if (method.getName().equalsIgnoreCase("service")) {
                System.out.println("服务之前");
                obj = method.invoke(waiter, args);
                System.out.println("服务之后");
            }
            return obj;
        }
    });
    waiterProxy.service();
    /*
    服务之前
    正在服务
    服务之后
    */
}

JDK 动态代理只能为实现了接口的类产生代理对象。

源码分析

从 newProxyInstance 方法看起:

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

     /*
      * 生成代理类的 class 文件
      */
     Class<?> cl = getProxyClass0(loader, intfs);

     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;
                 }
             });
         }
         /*
         使用代理类的构造器,传入参数 h(即我们实现的InvocationHandler类实例)创建代理类的实例并返回
          */
         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);
     }
 }

java.lang.reflect.Proxy.newProxyInstance

该方法的返回值是第 41 行返回的代理类实例,而这个代理类字节码文件创建工作都是在 18 行的 getProxyClass0 方法完成:

 private static Class<?> getProxyClass0(ClassLoader loader,

     // 如果被代理类实现的接口超出 65535 个则抛出异常
     if (interfaces.length > 65535) {
         throw new IllegalArgumentException("interface limit exceeded");
     }
     return proxyClassCache.get(loader, interfaces);
 }

java.lang.reflect.Proxy.getProxyClass0

接着查看第 7 行 proxyClassCache.get 方法:

 public V get(K key, P parameter) {
         Objects.requireNonNull(parameter);

         expungeStaleEntries();

         Object cacheKey = CacheKey.valueOf(key, refQueue);

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

         Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
         Supplier<V> supplier = valuesMap.get(subKey);
         Factory factory = null;

         while (true) {
             if (supplier != null) {
                 V value = supplier.get();
                 if (value != null) {
                     return value;
                 }
             }

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

             if (supplier == null) {
                 supplier = valuesMap.putIfAbsent(subKey, factory);
                 if (supplier == null) {
                     supplier = factory;
                 }
             } else {
                 if (valuesMap.replace(subKey, supplier, factory)) {
                     supplier = factory;
                 } else {
                     supplier = valuesMap.get(subKey);
                 }
             }
         }
     }

java.lang.reflect.WeakCache.get

直接从 22 行开始看,入眼就是一个死循环,它的出口在 26 行,当 supplier.get() 不为空时返回它的值,而 supplier 的赋值操作是在第 34-38 行,赋值后就会执行 22 行 supplier.get 方法:

 @Override
 public synchronized V get() {
     Supplier<V> supplier = valuesMap.get(subKey);
     if (supplier != this) {
         return null;
     }

     V value = null;
     try {
         value = Objects.requireNonNull(valueFactory.apply(key, parameter));
     } finally {
         if (value == null) {
             valuesMap.remove(subKey, this);
         }
     }

     assert value != null;

     CacheValue<V> cacheValue = new CacheValue<>(value);

     if (valuesMap.replace(subKey, this, cacheValue)) {

         reverseMap.put(cacheValue, Boolean.TRUE);
     } else {
         throw new AssertionError("Should not reach here");
     }
     return value;
 }

java.lang.reflect.WeakCache.Factory.get

这个方法的返回值在第 10 行,它的值为 valueFactory.apply(key, parameter) 的返回值,而此时 valueFactory 是 java.lang.reflect.Proxy.ProxyClassFactory 的实例,查看该实例的 apply 方法:

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

     Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
     for (Class<?> intf : interfaces) {
         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");
         }

         if (!interfaceClass.isInterface()) {
             throw new IllegalArgumentException(
                 interfaceClass.getName() + " is not an interface");
         }
         if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
             throw new IllegalArgumentException(
                 "repeated interface: " + interfaceClass.getName());
         }
     }

     String proxyPkg = null;
     int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

     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) {
         proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
     }

     long num = nextUniqueNumber.getAndIncrement();
     String proxyName = proxyPkg + proxyClassNamePrefix + num;

     byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
         proxyName, interfaces, accessFlags);
     try {
         return defineClass0(loader, proxyName,
                             proxyClassFile, 0, proxyClassFile.length);
     } catch (ClassFormatError e) {
         throw new IllegalArgumentException(e.toString());
     }
 }

java.lang.reflect.Proxy.ProxyClassFactory.apply

从 26-47 行实际上就是在拼接代理类包名,保存在变量 proxyPkg 中,在 50 行拼接出代理类全路径。

此处 52 行的 ProxyGenerator.generateProxyClass 方法才是真正生成代理类字节码文件的地方,将其保存在名为 proxyClassFile 的字节数组中。查看 ProxyGenerator.generateProxyClass 方法:

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

sun.misc.ProxyGenerator.generateProxyClass

在第 3 行通过 generateClassFile 方法创建代理类字节码文件,保存在字节数组。查看 generateClassFile 方法:

 private byte[] generateClassFile() {
         this.addProxyMethod(hashCodeMethod, Object.class);
         this.addProxyMethod(equalsMethod, Object.class);
         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);
         }

         if (this.methods.size() > 65535) {
             throw new IllegalArgumentException("method limit exceeded");
         } else if (this.fields.size() > 65535) {
             throw new IllegalArgumentException("field limit exceeded");
         } else {
             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);
             }
         }
     }

sun.misc.ProxyGenerator.generateClassFile

在起始 2-4 行可以看到,它还帮我们额外的代理了 hashCode、equals、toString 方法。
接着看 sun.misc.ProxyGenerator.generateProxyClass 的第 4 行,条件 saveGeneratedFiles 是一个布尔值,用于指定是否执行下面代码块的保存 class 文件到硬盘的功能,默认是 false。而 saveGeneratedFiles 的值实际上是取自

private static final boolean saveGeneratedFiles = (Boolean)AccessController.doPrivileged(new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"));

所以我们只要指定了它为 true,它就会帮我们保存字节码文件。

在 src 根目录下运行以下代码:

import com.zze.dao.impl.Waiter;
import com.zze.service.IWaiter;

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

public class Test {

    public static void main(String[] args) {
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        IWaiter waiter = new Waiter();
        Class<?>[] interfaces = Waiter.class.getInterfaces();
        IWaiter waiterProxy = (IWaiter) Proxy.newProxyInstance(Waiter.class.getClassLoader(), interfaces, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object obj = null;
                if (method.getName().equalsIgnoreCase("service")) {
                    System.out.println("服务之前");
                    obj = method.invoke(waiter, args);
                    System.out.println("服务之后");
                }
                return obj;
            }
        });
        waiterProxy.service();
    }
}

Test

接着在项目根目录下就会生成如下文件:

package com.sun.proxy;

import com.zze.service.IWaiter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements IWaiter {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $Proxy0(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 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 service() throws  {
        try {
            super.h.invoke(this, m3, (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"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.zze.service.IWaiter").getMethod("service");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

com.sun.proxy.$Proxy0

我们最后使用的代理对象就是 com.sun.proxy.$Proxy0 类的实例。

cglib动态代理

简介

CGLIB(Code Generation Library) 是一个开源项目!它是一个强大的,高性能,高质量的 Code 生成类库,它可以在运行期扩展 Java 类与实现 Java 接口。Hibernate 支持它来实现 PO(Persistent Object 持久化对象) 字节码的动态生成。

使用

1、引入 cglib 支持 jar,点击下载

2、编写被代理类:

package com.zze.service;

public class Waiter {
    public void service() {
        System.out.println("正在服务");
    }
}

com.zze.service.Waiter

3、使用 CGLIB 提供的动态代理:

@Test
public void test() {// 创建核心类对象
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(Waiter.class);
    enhancer.setCallback(new MethodInterceptor() {
        @Override
        public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            // 判断方法是否是 save
            if ("service".equals(method.getName())) {
                // 增强,权限校验
                System.out.println("权限校验...");
            }
            return methodProxy.invokeSuper(o, args);
        }
    });
    Waiter customerDaoProxy = (Waiter) enhancer.create();
    customerDaoProxy.service();
}

java之动态代理的更多相关文章

  1. java的动态代理机制详解

    在学习Spring的时候,我们知道Spring主要有两大思想,一个是IoC,另一个就是AOP,对于IoC,依赖注入就不用多说了,而对于Spring的核心AOP来说,我们不但要知道怎么通过AOP来满足的 ...

  2. java中动态代理实现机制

    前言: 代理模式是常用的java设计模式,它的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常会存在关联关系 ...

  3. Java特性-动态代理

    代理在开发中无处不在: 我们完成一个接口开发A,接口下有很多个实现类,这些类有些共同要处理的部分,比如每一个类都定义了接口A中的方法getXX(String name).我现在想把每次调用某个实现类的 ...

  4. java --- 设计模式 --- 动态代理

    Java设计模式——动态代理 java提供了动态代理的对象,本文主要探究它的实现, 动态代理是AOP(面向切面编程, Aspect Oriented Programming)的基础实现方式, 动态代理 ...

  5. java的动态代理机制

    前几天看到java的动态代理机制,不知道是啥玩意,然后看了看.死活不知道 invoke(Object proxy, Method m, Object[] args)种的proxy是个什么东西,放在这里 ...

  6. java中动态代理

    一.在java中怎样实现动态代理 1.我们要有一个接口,还要有一个接口的实现类,而这个实现类呢就是我们要代理的对象 接口: package org.dynamicproxy.test; public ...

  7. Java的动态代理机制详解(转)

    在学习Spring的时候,我们知道Spring主要有两大思想,一个是IoC,另一个就是AOP,对于IoC,依赖注入就不用多说了,而对于Spring的核心AOP来说,我们不但要知道怎么通过AOP来满足的 ...

  8. (转)java的动态代理机制详解

    原文出自:http://www.cnblogs.com/xiaoluo501395377/p/3383130.html 在学习Spring的时候,我们知道Spring主要有两大思想,一个是IoC,另一 ...

  9. [转载] java的动态代理机制详解

    转载自http://www.cnblogs.com/xiaoluo501395377/p/3383130.html 代理模式 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代 ...

  10. 【译】11. Java反射——动态代理

    原文地址:http://tutorials.jenkov.com/java-reflection/dynamic-proxies.html 博主最近比较忙,争取每周翻译四篇.等不急的请移步原文网页. ...

随机推荐

  1. MYSQL 优化器 源码解析

    http://www.unofficialmysqlguide.com/introduction.html https://dev.mysql.com/doc/refman/8.0/en/explai ...

  2. 基于Vue element-ui实现支持多级纵向动态表头的仿表格布局

    [本文出自天外归云的博客园] 需求图示如下,多级纵向动态表头表格: 我的思路是用element-ui的layout实现,做出一个仿造表格,能够支持动态的.多级的.纵向的表头: <template ...

  3. python 中有趣的库tqdm

    Tqdm 是 Python 进度条库,可以在 Python 长循环中添加一个进度提示信息用法:tqdm(iterator) # 方法1: import time from tqdm import tq ...

  4. SparkStreaming基本架构及使用

    1.简介 Spark Streaming处理的数据流图: Spark Streaming在内部的处理机制是,接收实时流的数据,并根据一定的时间间隔拆分成一批批的数据,然后通过Spark Engine处 ...

  5. Spark RDD、DataFrame原理及操作详解

    RDD是什么? RDD (resilientdistributed dataset),指的是一个只读的,可分区的分布式数据集,这个数据集的全部或部分可以缓存在内存中,在多次计算间重用. RDD内部可以 ...

  6. ubuntu安装thrift

    ubuntu环境下安装thrift-0.10.0 1.解压 2.编译安装 ./configure -with-cpp -with-boost -without-python -without-csha ...

  7. Object.assign方法的使用

    https://www.cnblogs.com/chenyablog/p/6930596.html

  8. Debian/Ubuntu pip default install to $HOME/.local

    pip default install to $HOME/.local on Debian/Ubuntu After pip 8.1.1-2 on Debian or Ubuntu you can p ...

  9. [BTS] BizTalk EDI character set and separator settings

    最近一个项目上遇到需要对EDI头.分隔符.小数点等配置项进行设置. 这里只记录一下结果,原理不记了,多看MSDN. UNB1中的信息在BizTalk中是不会体现在EDI输出文件中的,双方协商好即可. ...

  10. Android的TextView设置加粗对汉字无效

    //not work textView.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD)); //work! static public voi ...