摘要

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

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

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. np.corrcoef()方法计算数据皮尔逊积矩相关系数(Pearson's r)

    上一篇通过公式自己写了一个计算两组数据的皮尔逊积矩相关系数(Pearson's r)的方法,但np已经提供了一个用于计算皮尔逊积矩相关系数(Pearson's r)的方法 np.corrcoef()  ...

  2. nginx实现限速

    项目中有一个需求,需要限制每个容器的网速,避免某些容器占用太多资源,导致其他容器无法使用,但是docker对于网速的限制支持的有点弱,由于容器中的所有进程和APP的交互都是通过nginx的,所以就想到 ...

  3. python线程池(threadpool)模块使用笔记 .python 线程池使用推荐

    一.安装与简介 pip install threadpool pool = ThreadPool(poolsize) requests = makeRequests(some_callable, li ...

  4. 第四百零六节,自定义用户表类来继承Django的用户表类,

    第四百零六节,自定义用户表类来继承Django的用户表类, models.py from django.db import models # Create your models here. from ...

  5. 某公司面试java试题之【一】,看看吧,说不定就是你将要做的题

  6. Linux 依据关键字查找正在运行的进程

    ps aux |grep tm1s

  7. linux 使用不安全的sprintf函数,存储字符越界导致程序莫名崩溃问题

    linux c++编程 问题背景: 在处理一个公共模块的代码中,其中有以下代码片段 //代码片段-组合一组字符串并存放到szSignKey数组中 ] = {}; sprintf(szSignKey, ...

  8. Windows下Codeblocks调试Cocos2d-x项目体验(一次失败的体验)

    很久之前的一篇文章有介绍过在Ubuntu下安装Cocos2d-x3.11并使用Codeblock调试Cocos2d-x程序:http://www.cnblogs.com/moonlightpoet/p ...

  9. Qt编写自定义控件10-云台仪表盘

    前言 做过安防视频监控的同学都清楚,在视频监控系统软件上都可以看到一个云台控制区域,可以对球机进行下下左右等八个方位的运动控制,还可以进行复位,一般都是美工作图好,然后贴图的形式加入到软件中,好处是程 ...

  10. javascript--面试题

    (1)javaScript怎么清空数组? 如 var arrayList = ['a','b','c','d','e','f']; 怎么清空 arrayList 方法1:直接改变arrayList所指 ...