天天看点

Java JDK 动态代理使用及实现原理分析

一、什么是代理?

代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。

代理模式UML图:

Java JDK 动态代理使用及实现原理分析
简单结构示意图:
Java JDK 动态代理使用及实现原理分析

为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间,从而在设计上获得了更大的灵活性。Java 动态代理机制以巧妙的方式近乎完美地实践了代理模式的设计理念。

二、Java 动态代理类 

Java动态代理类位于java.lang.reflect包下,一般主要涉及到以下两个类:

(1)Interface InvocationHandler:该接口中仅定义了一个方法

[java] view plain copy

  1. publicobject invoke(Object obj,Method method, Object[] args)  

在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,如上例中的request(),args为该方法的参数数组。这个抽象方法在代理类中动态实现。

(2)Proxy:该类即为动态代理类,其中主要包含以下内容:

protected Proxy(InvocationHandler h):构造函数,用于给内部的h赋值。

static Class getProxyClass (ClassLoaderloader, Class[] interfaces):获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。

static Object newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h):返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口中声明过的方法)

所谓DynamicProxy是这样一种class:它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些interface。你当然可以把该class的实例当作这些interface中的任何一个来用。当然,这个DynamicProxy其实就是一个Proxy,它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。

在使用动态代理类时,我们必须实现InvocationHandler接口

通过这种方式,被代理的对象(RealSubject)可以在运行时动态改变,需要控制的接口(Subject接口)可以在运行时改变,控制的方式(DynamicSubject类)也可以动态改变,从而实现了非常灵活的动态代理关系。

动态代理步骤:

1.创建一个实现接口InvocationHandler的类,它必须实现invoke方法

2.创建被代理的类以及接口

3.通过Proxy的静态方法

newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h)创建一个代理

4.通过代理调用方法

三、JDK的动态代理怎么使用?

1、需要动态代理的接口:

  1. package jiankunking;  
  2. /** 
  3.  * 需要动态代理的接口 
  4.  */  
  5. public interface Subject  
  6. {  
  7.     /** 
  8.      * 你好 
  9.      * 
  10.      * @param name 
  11.      * @return 
  12.      */  
  13.     public String SayHello(String name);  
  14.      * 再见 
  15.     public String SayGoodBye();  
  16. }  

2、需要代理的实际对象

  1.  * 实际对象 
  2. public class RealSubject implements Subject  
  3.     public String SayHello(String name)  
  4.     {  
  5.         return "hello " + name;  
  6.     }  
  7.     public String SayGoodBye()  
  8.         return " good bye ";  

3、调用处理器实现类(有木有感觉这里就是传说中的AOP啊)

  1. import java.lang.reflect.InvocationHandler;  
  2. import java.lang.reflect.Method;  
  3.  * 调用处理器实现类 
  4.  * 每次生成动态代理类对象时都需要指定一个实现了该接口的调用处理器对象 
  5. public class InvocationHandlerImpl implements InvocationHandler  
  6.      * 这个就是我们要代理的真实对象 
  7.     private Object subject;  
  8.      * 构造方法,给我们要代理的真实对象赋初值 
  9.      * @param subject 
  10.     public InvocationHandlerImpl(Object subject)  
  11.         this.subject = subject;  
  12.      * 该方法负责集中处理动态代理类上的所有方法调用。 
  13.      * 调用处理器根据这三个参数进行预处理或分派到委托类实例上反射执行 
  14.      * @param proxy  代理类实例 
  15.      * @param method 被调用的方法对象 
  16.      * @param args   调用参数 
  17.      * @throws Throwable 
  18.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable  
  19.         //在代理真实对象前我们可以添加一些自己的操作  
  20.         System.out.println("在调用之前,我要干点啥呢?");  
  21.         System.out.println("Method:" + method);  
  22.         //当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用  
  23.         Object returnValue = method.invoke(subject, args);  
  24.         //在代理真实对象后我们也可以添加一些自己的操作  
  25.         System.out.println("在调用之后,我要干点啥呢?");  
  26.         return returnValue;  

4、测试

  1. import java.lang.reflect.Proxy;  
  2.  * 动态代理演示 
  3. public class DynamicProxyDemonstration  
  4.     public static void main(String[] args)  
  5.         //代理的真实对象  
  6.         Subject realSubject = new RealSubject();  
  7.         /** 
  8.          * InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发 
  9.          * 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用. 
  10.          * 即:要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法 
  11.          */  
  12.         InvocationHandler handler = new InvocationHandlerImpl(realSubject);  
  13.         ClassLoader loader = handler.getClass().getClassLoader();  
  14.         Class[] interfaces = realSubject.getClass().getInterfaces();  
  15.          * 该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例 
  16.         Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);  
  17.         System.out.println("动态代理对象的类型:"+subject.getClass().getName());  
  18.         String hello = subject.SayHello("jiankunking");  
  19.         System.out.println(hello);  
  20. //        String goodbye = subject.SayGoodBye();  
  21. //        System.out.println(goodbye);  

5、输出结果如下:

Java JDK 动态代理使用及实现原理分析

演示demo下载地址:javascript:void(0)

四、动态代理怎么实现的?

从使用代码中可以看出,关键点在:

  1. Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);  

通过跟踪提示代码可以看出:当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用。

也就是说,当代码执行到:

subject.SayHello("jiankunking")这句话时,会自动调用InvocationHandlerImpl的invoke方法。这是为啥呢?

======================横线之间的是代码跟分析的过程,不想看的朋友可以直接看结论=====================================

既然生成代理对象是用的Proxy类的静态方newProxyInstance,那么我们就去它的源码里看一下它到底都做了些什么? 

  1. public static Object newProxyInstance(ClassLoader loader,  
  2.                                          Class<?>[] interfaces,  
  3.                                          InvocationHandler h)  
  4.        throws IllegalArgumentException  
  5.    {  
  6.     //检查h 不为空,否则抛异常  
  7.        Objects.requireNonNull(h);  
  8.        final Class<?>[] intfs = interfaces.clone();  
  9.        final SecurityManager sm = System.getSecurityManager();  
  10.        if (sm != null) {  
  11.            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);  
  12.        }  
  13.        /* 
  14.         * 获得与指定类装载器和一组接口相关的代理类类型对象 
  15.         */  
  16.        Class<?> cl = getProxyClass0(loader, intfs);  
  17.         * 通过反射获取构造函数对象并生成代理类实例 
  18.        try {  
  19.            if (sm != null) {  
  20.                checkNewProxyPermission(Reflection.getCallerClass(), cl);  
  21.            }  
  22.         //获取代理对象的构造方法(也就是$Proxy0(InvocationHandler h))   
  23.            final Constructor<?> cons = cl.getConstructor(constructorParams);  
  24.            final InvocationHandler ih = h;  
  25.            if (!Modifier.isPublic(cl.getModifiers())) {  
  26.                AccessController.doPrivileged(new PrivilegedAction<Void>() {  
  27.                    public Void run() {  
  28.                        cons.setAccessible(true);  
  29.                        return null;  
  30.                    }  
  31.                });  
  32.         //生成代理类的实例并把InvocationHandlerImpl的实例传给它的构造方法  
  33.            return cons.newInstance(new Object[]{h});  
  34.        } catch (IllegalAccessException|InstantiationException e) {  
  35.            throw new InternalError(e.toString(), e);  
  36.        } catch (InvocationTargetException e) {  
  37.            Throwable t = e.getCause();  
  38.            if (t instanceof RuntimeException) {  
  39.                throw (RuntimeException) t;  
  40.            } else {  
  41.                throw new InternalError(t.toString(), t);  
  42.        } catch (NoSuchMethodException e) {  
  43.    }  

我们再进去getProxyClass0方法看一下:

  1.     * Generate a proxy class.  Must call the checkProxyAccess method 
  2.     * to perform permission checks before calling this. 
  3.     */  
  4.    private static Class<?> getProxyClass0(ClassLoader loader,  
  5.                                           Class<?>... interfaces) {  
  6.        if (interfaces.length > 65535) {  
  7.            throw new IllegalArgumentException("interface limit exceeded");  
  8.        // If the proxy class defined by the given loader implementing  
  9.        // the given interfaces exists, this will simply return the cached copy;  
  10.        // otherwise, it will create the proxy class via the ProxyClassFactory  
  11.        return proxyClassCache.get(loader, interfaces);  

真相还是没有来到,继续,看一下proxyClassCache

  1.      * a cache of proxy classes 
  2.     private static final WeakCache<ClassLoader, Class<?>[], Class<?>>  
  3.         proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());  

奥,原来用了一下缓存啊

那么它对应的get方法啥样呢?

  1.     * Look-up the value through the cache. This always evaluates the 
  2.     * {@code subKeyFactory} function and optionally evaluates 
  3.     * {@code valueFactory} function if there is no entry in the cache for given 
  4.     * pair of (key, subKey) or the entry has already been cleared. 
  5.     * 
  6.     * @param key       possibly null key 
  7.     * @param parameter parameter used together with key to create sub-key and 
  8.     *                  value (should not be null) 
  9.     * @return the cached value (never null) 
  10.     * @throws NullPointerException if {@code parameter} passed in or 
  11.     *                              {@code sub-key} calculated by 
  12.     *                              {@code subKeyFactory} or {@code value} 
  13.     *                              calculated by {@code valueFactory} is null. 
  14.    public V get(K key, P parameter) {  
  15.        Objects.requireNonNull(parameter);  
  16.        expungeStaleEntries();  
  17.        Object cacheKey = CacheKey.valueOf(key, refQueue);  
  18.        // lazily install the 2nd level valuesMap for the particular cacheKey  
  19.        ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);  
  20.        if (valuesMap == null) {  
  21.         //putIfAbsent这个方法在key不存在的时候加入一个值,如果key存在就不放入  
  22.            ConcurrentMap<Object, Supplier<V>> oldValuesMap  
  23.                = map.putIfAbsent(cacheKey,  
  24.                                  valuesMap = new ConcurrentHashMap<>());  
  25.            if (oldValuesMap != null) {  
  26.                valuesMap = oldValuesMap;  
  27.        // create subKey and retrieve the possible Supplier<V> stored by that  
  28.        // subKey from valuesMap  
  29.        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));  
  30.        Supplier<V> supplier = valuesMap.get(subKey);  
  31.        Factory factory = null;  
  32.        while (true) {  
  33.            if (supplier != null) {  
  34.                // supplier might be a Factory or a CacheValue<V> instance  
  35.                V value = supplier.get();  
  36.                if (value != null) {  
  37.                    return value;  
  38.                }  
  39.            // else no supplier in cache  
  40.            // or a supplier that returned null (could be a cleared CacheValue  
  41.            // or a Factory that wasn't successful in installing the CacheValue)  
  42.            // lazily construct a Factory  
  43.            if (factory == null) {  
  44.                factory = new Factory(key, parameter, subKey, valuesMap);  
  45.            if (supplier == null) {                
  46.                supplier = valuesMap.putIfAbsent(subKey, factory);  
  47.                if (supplier == null) {  
  48.                    // successfully installed Factory  
  49.                    supplier = factory;  
  50.                // else retry with winning supplier  
  51.                if (valuesMap.replace(subKey, supplier, factory)) {  
  52.                    // successfully replaced  
  53.                    // cleared CacheEntry / unsuccessful Factory  
  54.                    // with our Factory  
  55.                } else {  
  56.                    // retry with current supplier  
  57.                    supplier = valuesMap.get(subKey);  

我们可以看到它调用了 supplier.get(); 获取动态代理类,其中supplier是Factory,这个类定义在WeakCach的内部。

来瞅瞅,get里面又做了什么?

  1. public synchronized V get() { // serialize access  
  2.            // re-check  
  3.            Supplier<V> supplier = valuesMap.get(subKey);  
  4.            if (supplier != this) {  
  5.                // something changed while we were waiting:  
  6.                // might be that we were replaced by a CacheValue  
  7.                // or were removed because of failure ->  
  8.                // return null to signal WeakCache.get() to retry  
  9.                // the loop  
  10.                return null;  
  11.            // else still us (supplier == this)  
  12.            // create new value  
  13.            V value = null;  
  14.            try {  
  15.                value = Objects.requireNonNull(valueFactory.apply(key, parameter));  
  16.            } finally {  
  17.                if (value == null) { // remove us on failure  
  18.                    valuesMap.remove(subKey, this);  
  19.            // the only path to reach here is with non-null value  
  20.            assert value != null;  
  21.            // wrap value with CacheValue (WeakReference)  
  22.            CacheValue<V> cacheValue = new CacheValue<>(value);  
  23.            // try replacing us with CacheValue (this should always succeed)  
  24.            if (valuesMap.replace(subKey, this, cacheValue)) {  
  25.                // put also in reverseMap  
  26.                reverseMap.put(cacheValue, Boolean.TRUE);  
  27.                throw new AssertionError("Should not reach here");  
  28.            // successfully replaced us with new CacheValue -> return the value  
  29.            // wrapped by it  
  30.            return value;  

发现重点还是木有出现,但我们可以看到它调用了valueFactory.apply(key, parameter)方法:

  1.     * A factory function that generates, defines and returns the proxy class given 
  2.     * the ClassLoader and array of interfaces. 
  3.    private static final class ProxyClassFactory  
  4.        implements BiFunction<ClassLoader, Class<?>[], Class<?>>  
  5.        // prefix for all proxy class names  
  6.        private static final String proxyClassNamePrefix = "$Proxy";  
  7.        // next number to use for generation of unique proxy class names  
  8.        private static final AtomicLong nextUniqueNumber = new AtomicLong();  
  9.        @Override  
  10.        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {  
  11.            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);  
  12.            for (Class<?> intf : interfaces) {  
  13.                /* 
  14.                 * Verify that the class loader resolves the name of this 
  15.                 * interface to the same Class object. 
  16.                 */  
  17.                Class<?> interfaceClass = null;  
  18.                try {  
  19.                    interfaceClass = Class.forName(intf.getName(), false, loader);  
  20.                } catch (ClassNotFoundException e) {  
  21.                if (interfaceClass != intf) {  
  22.                    throw new IllegalArgumentException(  
  23.                        intf + " is not visible from class loader");  
  24.                 * Verify that the Class object actually represents an 
  25.                 * interface. 
  26.                if (!interfaceClass.isInterface()) {  
  27.                        interfaceClass.getName() + " is not an interface");  
  28.                 * Verify that this interface is not a duplicate. 
  29.                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {  
  30.                        "repeated interface: " + interfaceClass.getName());  
  31.            String proxyPkg = null;     // package to define proxy class in  
  32.            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;  
  33.            /* 
  34.             * Record the package of a non-public proxy interface so that the 
  35.             * proxy class will be defined in the same package.  Verify that 
  36.             * all non-public proxy interfaces are in the same package. 
  37.             */  
  38.                int flags = intf.getModifiers();  
  39.                if (!Modifier.isPublic(flags)) {  
  40.                    accessFlags = Modifier.FINAL;  
  41.                    String name = intf.getName();  
  42.                    int n = name.lastIndexOf('.');  
  43.                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));  
  44.                    if (proxyPkg == null) {  
  45.                        proxyPkg = pkg;  
  46.                    } else if (!pkg.equals(proxyPkg)) {  
  47.                        throw new IllegalArgumentException(  
  48.                            "non-public interfaces from different packages");  
  49.            if (proxyPkg == null) {  
  50.                // if no non-public proxy interfaces, use com.sun.proxy package  
  51.                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";  
  52.             * Choose a name for the proxy class to generate. 
  53.            long num = nextUniqueNumber.getAndIncrement();  
  54.            String proxyName = proxyPkg + proxyClassNamePrefix + num;  
  55.             * Generate the specified proxy class. 
  56.            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(  
  57.                proxyName, interfaces, accessFlags);  
  58.                return defineClass0(loader, proxyName,  
  59.                                    proxyClassFile, 0, proxyClassFile.length);  
  60.            } catch (ClassFormatError e) {  
  61.                 * A ClassFormatError here means that (barring bugs in the 
  62.                 * proxy class generation code) there was some other 
  63.                 * invalid aspect of the arguments supplied to the proxy 
  64.                 * class creation (such as virtual machine limitations 
  65.                 * exceeded). 
  66.                throw new IllegalArgumentException(e.toString());  

通过看代码终于找到了重点:

  1. //生成字节码  
  2. byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);  

那么接下来我们也使用测试一下,使用这个方法生成的字节码是个什么样子:

  1. import sun.misc.ProxyGenerator;  
  2. import java.io.File;  
  3. import java.io.FileNotFoundException;  
  4. import java.io.FileOutputStream;  
  5. import java.io.IOException;  
  6.         // 将生成的字节码保存到本地,  
  7.         createProxyClassFile();  
  8.     private static void createProxyClassFile(){  
  9.         String name = "ProxySubject";  
  10.         byte[] data = ProxyGenerator.generateProxyClass(name,new Class[]{Subject.class});  
  11.         FileOutputStream out =null;  
  12.         try {  
  13.             out = new FileOutputStream(name+".class");  
  14.             System.out.println((new File("hello")).getAbsolutePath());  
  15.             out.write(data);  
  16.         } catch (FileNotFoundException e) {  
  17.             e.printStackTrace();  
  18.         } catch (IOException e) {  
  19.         }finally {  
  20.             if(null!=out) try {  
  21.                 out.close();  
  22.             } catch (IOException e) {  
  23.                 e.printStackTrace();  
  24.             }  
  25.         }  

我们用jd-jui 工具将生成的字节码反编译:

  1. import java.lang.reflect.UndeclaredThrowableException;  
  2. import jiankunking.Subject;  
  3. public final class ProxySubject  
  4.   extends Proxy  
  5.   implements Subject  
  6.   private static Method m1;  
  7.   private static Method m3;  
  8.   private static Method m4;  
  9.   private static Method m2;  
  10.   private static Method m0;  
  11.   public ProxySubject(InvocationHandler paramInvocationHandler)  
  12.   {  
  13.     super(paramInvocationHandler);  
  14.   }  
  15.   public final boolean equals(Object paramObject)  
  16.     try  
  17.       return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();  
  18.     catch (Error|RuntimeException localError)  
  19.       throw localError;  
  20.     catch (Throwable localThrowable)  
  21.       throw new UndeclaredThrowableException(localThrowable);  
  22.   public final String SayGoodBye()  
  23.       return (String)this.h.invoke(this, m3, null);  
  24.   public final String SayHello(String paramString)  
  25.       return (String)this.h.invoke(this, m4, new Object[] { paramString });  
  26.   public final String toString()  
  27.       return (String)this.h.invoke(this, m2, null);  
  28.   public final int hashCode()  
  29.       return ((Integer)this.h.invoke(this, m0, null)).intValue();  
  30.   static  
  31.       m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });  
  32.       m3 = Class.forName("jiankunking.Subject").getMethod("SayGoodBye", new Class[0]);  
  33.       m4 = Class.forName("jiankunking.Subject").getMethod("SayHello", new Class[] { Class.forName("java.lang.String") });  
  34.       m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);  
  35.       m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);  
  36.       return;  
  37.     catch (NoSuchMethodException localNoSuchMethodException)  
  38.       throw new NoSuchMethodError(localNoSuchMethodException.getMessage());  
  39.     catch (ClassNotFoundException localClassNotFoundException)  
  40.       throw new NoClassDefFoundError(localClassNotFoundException.getMessage());  

这就是最终真正的代理类,它继承自Proxy并实现了我们定义的Subject接口

也就是说:

这里的subject实际是这个类的一个实例,那么我们调用它的:

  1. public final String SayHello(String paramString)  

就是调用我们定义的InvocationHandlerImpl的 invoke方法:

Java JDK 动态代理使用及实现原理分析

五、结论

到了这里,终于解答了:      
subject.SayHello("jiankunking")这句话时,为什么会自动调用InvocationHandlerImpl的invoke方法?      
因为JDK生成的最终真正的代理类,它继承自Proxy并实现了我们定义的Subject接口,在实现Subject接口方法的内部,通过反射调用了      
InvocationHandlerImpl的invoke方法。      
包含生成本地class文件的demo:javascript:void(0)      
通过分析代码可以看出Java 动态代理,具体有如下四步骤:      
  1. 通过实现 InvocationHandler 接口创建自己的调用处理器;
  2. 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
  3. 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
  4. 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。