動態代理實現原理

裝飾模式 vs (靜態)代理模式中提到,在靜態代理模式中,針對每個須要被代理的類都要在編譯前就提早寫好一個代理類,這樣作增長了類管理的複雜性,若是咱們能夠在運行期間動態的來生成這個代理類,就會方便不少,這就是動態代理模式的核心思想,也是Spring中AOP(Aspect Oriented Programming)的實現原理。動態代理有兩種實現方法:jdk動態代理和cglib動態代理,下面分別來具體看一下:java

jdk動態代理

咱們知道,在java中若是想在運行期動態的生成一個類,就要藉助反射機制。jdk動態代理就是經過java.lang.reflect.Proxy利用反射機制提供了一種原生的動態代理模式,它提供了一個靜態方法:express

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

        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        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;
                    }
                });
            }
            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);
        }
    }

它的三個參數分別是:apache

  • loader the class loader to define the proxy class
  • interfaces the list of interfaces for the proxy class to implement
  • the invocation handler to dispatch method invocations to

其中Class<?> cl = getProxyClass0(loader, intfs);最終會調用Proxy的內部類ProxyClassFactory,而後調用ProxyGenerator裏的generateProxyClass生成Class字節碼數組:segmentfault

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

最後利用defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);返回類實例。而後利用這個類傳入InvocationHandler參數構建一個代理類實例。
在運行當前main方法的路徑下建立com/sun/proxy目錄,並建立一個\$Proxy0.class文件,而後設置sun.misc.ProxyGenerator.saveGeneratedFiles系統屬性爲true,反編譯\$Proxy0.class文件能夠看到:數組

public final class $Proxy0 extends Proxy implements UserManager {
  private static Method m1;
  private static Method m3;
  private static Method m0;
  private static Method m2;
 
  public $Proxy0(InvocationHandler paramInvocationHandler) {
    super(paramInvocationHandler);
  }
 
  public final boolean equals(Object paramObject) {
    try {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    }
    catch (Error|RuntimeException localError) {
      throw localError;
    }
    catch (Throwable localThrowable) {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
 
  public final void addUser(String paramString) {
    try {
      this.h.invoke(this, m3, new Object[] { paramString });
      return;
    }
    catch (Error|RuntimeException localError) {
      throw localError;
    }
    catch (Throwable localThrowable) {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
 
  public final int hashCode() {
    try {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    catch (Error|RuntimeException localError) {
      throw localError;
    }
    catch (Throwable localThrowable) {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
 
  public final String toString() {
    try {
      return (String)this.h.invoke(this, m2, null);
    }
    catch (Error|RuntimeException localError) {
      throw localError;
    }
    catch (Throwable localThrowable) {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
 
  static {
    try {
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m3 = Class.forName("com.leon.proxy.UserManager").getMethod("addUser", new Class[] { Class.forName("java.lang.String") });
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException) {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException) {
      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
  }
}

能夠看到這個代理類裏對於方法的調用都會去調用傳入InvocationHandler的invoke方法。因此咱們只須要實現這個接口的invoke方法,就能夠實現任意被代理類方法的攔截和擴展。最後附上示例代碼:app

public interface UserManager {
    void addUser(String userName);
}
public class UserManagerImpl implements UserManager {
    @Override
    public void addUser(String userName) {
        System.out.println("Add user: " + userName);
    }
}
public class LogHandler implements InvocationHandler {
    private Object targetObject;
    private Object newProxyInstance(Object targetObject){
        this.targetObject=targetObject;
        return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
                targetObject.getClass().getInterfaces(),this);
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("=========start=========");
        Object ret=method.invoke(targetObject, args);
        System.out.println("=========end=========");
        return ret;
    }
    public static void main(String[] args){
        LogHandler logHandler=new LogHandler();
        UserManager userManager=(UserManager)logHandler.newProxyInstance(new UserManagerImpl());
        userManager.addUser("Leon");
    }
}

經過實現InvocationHandler接口,即可以對任意實現了接口的類進行代理,若是要對沒有實現接口的類進行代理可使用下面的方法。less

cglib動態代理

經過cglib(Code Generation Library)第三方庫來實現的動態代理,它的底層使用ASM(Java bytecode manipulation and analysis framework)利用繼承的方法在內存中動態的生成被代理類的子類,解決了jdk動態代理要求被代理類必須實現接口的侷限,且運行速度要遠遠快於jdk動態代理。下面假設UserManagerImpl沒有實現接口:ide

public class UserManagerImpl {
    public void addUser(String userName) {
        System.out.println("Add user: " + userName);
    }
}

首先實現一個MethodInterceptor接口,相似於InvocationHandler接口:ui

class LogInterceptor implements MethodInterceptor{
  ...
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("=========start=========");
        Object ret = proxy.invokeSuper(obj, args);
        System.out.println("=========end=========");
        return ret;
    }
}

而後利用cglib的Enhancer來生成代理類:this

Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserManagerImpl.class);
enhancer.setCallback(new LogInterceptor());
 
UserManagerImpl userManagerImpl = (UserManagerImpl)enhancer.create();
userManagerImpl.addUser("Leon");

這裏須要注意,因爲是經過繼承來實現代理,因此不能代理final類,也不能代理final方法。若是反編譯生成的代理類,能夠看到:

public class UserManagerImpl$$EnhancerByCGLIB$$e4856e83
  extends UserManagerImpl
  implements Factory
{
  ...
  private MethodInterceptor CGLIB$CALLBACK_0;
  ...
 
  public final String addUser(String userName)
  {
    ...
    MethodInterceptor var3= CGLIB$CALLBACK_0;
    if (var3 != null) {
      return (String)var3.intercept(this, CGLIB$addUser$0$Method, new Object[] {userName}, CGLIB$sayHello$0$Proxy);
    }
    return super.addUser(userName);
  }
  ...
}

代理類繼承了被代理類並實現了net.sf.cglib.proxy.Factor接口,執行方法是若是有MethodInterceptor就調用其intercept方法,若有沒有就調用父類也就是被代理類方法。最後附上MethodInterceptor接口的定義:

/*
 * Copyright 2002,2003 The Apache Software Foundation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.sf.cglib.proxy;
 
/**
 * General-purpose {@link Enhancer} callback which provides for "around advice".
 * @author Juozas Baliuka <a href="mailto:baliuka@mwm.lt">baliuka@mwm.lt</a>
 * @version $Id: MethodInterceptor.java,v 1.8 2004/06/24 21:15:20 herbyderby Exp $
 */
public interface MethodInterceptor
extends Callback
{
    /**
     * All generated proxied methods call this method instead of the original method.
     * The original method may either be invoked by normal reflection using the Method object,
     * or by using the MethodProxy (faster).
     * @param obj "this", the enhanced object
     * @param method intercepted Method
     * @param args argument array; primitive types are wrapped
     * @param proxy used to invoke super (non-intercepted method); may be called
     * as many times as needed
     * @throws Throwable any exception may be thrown; if so, super method will not be invoked
     * @return any value compatible with the signature of the proxied method. Method returning void will ignore this value.
     * @see MethodProxy
     */    
    public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
                               MethodProxy proxy) throws Throwable;
}
相關文章
相關標籤/搜索