摘要

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

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

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. Ajax 请求头中常见content-type

    四种常见的 POST 提交数据方式 HTTP 协议是以 ASCII 码传输,建立在 TCP/IP 协议之上的应用层规范.规范把 HTTP 请求分为三个部分:状态行.请求头.消息主体.协议规定 POST ...

  2. Hadoop集群datanode磁盘不均衡的解决方案【转】

    一.引言: Hadoop的HDFS集群非常容易出现机器与机器之间磁盘利用率不平衡的情况,比如集群中添加新的数据节点,节点与节点之间磁盘大小不一样等等.当hdfs出现不平衡状况的时候,将引发很多问题,比 ...

  3. 【转】pymongo实现模糊查询

    pymongo 模糊匹配查询在mongo中这样实现 {'asr':/若琪/} 使用pymongo 两种实现方式 1.import re {'asr':re.compile('若琪')} 2.{'asr ...

  4. 力导向图Demo

    <html> <head> <meta charset="utf-8"> <title>力导向图</title> < ...

  5. spring源码:web容器启动

    web项目中可以集成spring的ApplicationContext进行bean的管理,这样使用起来bean更加便捷,能够利用到很多spring的特性.我们比较常用的web容器有jetty,tomc ...

  6. LiveSwitch白皮书

    https://www.frozenmountain.com/media/1224/frozen-mountain-software-liveswitch-white-paper.pdf —————— ...

  7. myeclipse16怎么去掉项目中的CodeLive Panel?

    http://www.jb51.net/softjc/524823.html —————————————————————————————————————————————————— 在Servers视图 ...

  8. WebSphere MQ中的CCSID

    CCSID是一个字符集的标识.作为unicode标准通过定义一个字符集内每个字符要对应那个数字值的方式定义了一个字符集.这说明CCSID就是一个定义字符集顺序的标识数码罢了.IBM的字符标识架构在文档 ...

  9. Win7 vs2017 WDK 1803 1809 驱动开发 出错 KMDF

    一.编译出错, 1. 包含头文件出错 解决方案: 需要下载1803 的wdk  最新的1809会出一堆错误 安装程序显示是10.0.17134.1安装完成后是10.0.17134.0 2. Inf2C ...

  10. RTX参数配置

        RTX操作系统的配置工作是通过配置文件RTX_Conf_CM.c实现.     在MDK工程中打开文件RTX_Conf_CM.c,可以看到如下图5.2所示的工程配置向导:  20 Task C ...