Java 动态代理机制简单理解

  Spring有两个核心的思想,一个是IOC,另一个就是AOP,而这个AOP就是建立在JAVA动态代理基础上的,下面先用一个简单的示例来说明动态代理的用法,然后简单叙述动态代理实现的原理。

一、示例

实现代理有四个步骤

1、创建一个接口

public interface Login {
    void validate();
    void login();
}

2、编写这个接口的实现类,这个类里面只包含业务方法

public class LoginImpl implements Login{

    @Override
    public void validate() {
        // TODO Auto-generated method stub
        System.out.println("validate");
    }

    @Override
    public void login() {
        // TODO Auto-generated method stub
        System.out.println("login");
    }

}

3、创建代理类,实现InvacationHandler,通过这个代理类可以动态创建代理对象,并且完成相关业务。

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


public class LoginProxy implements InvocationHandler {
    private Object proxyObj;
    public  LoginProxy(Object obj)
    {
        this.proxyObj=obj;
    }
    
    public static Object bind(Object obj)
    {
        Class clz=obj.getClass();
        return Proxy.newProxyInstance(clz.getClassLoader(), clz.getInterfaces(), new LoginProxy(obj));
    }
    @Override
    public Object invoke(Object arg0, Method arg1, Object[] arg2) throws Throwable {
        // TODO Auto-generated method stub
        login(arg1);
        Object res=arg1.invoke(proxyObj, arg2);
        logout(arg1);
        return res;
    }
    
    private void login(Method method)
    {
        System.out.println("before "+method.getName());
    }
    
    private void logout(Method method)
    {
        System.out.println("after "+method.getName());
    }
}

4、创建测试类。

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

import sun.misc.ProxyGenerator;
public class Main {
    public static void main(String[] args) {
        //生成一个实现Login接口的类的字节码数组,测试用
        byte[] clazzFile = ProxyGenerator.generateProxyClass("$Proxy11", LoginImpl.class.getInterfaces());  
        OutputStream os=null;
        try {
            //生成的class文件放在bin文件夹里面
            os=new FileOutputStream(Main.class.getClassLoader().getResource("").getPath()+"/$Proxy11.class");
            os.write(clazzFile);
            os.flush();
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }finally{
            try {
                os.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        //测试动态代理,生成一个代理对象,这个代理对象实现了LoginImpl实现的接口
        Login login=(Login)LoginProxy.bind(new LoginImpl());
        //代理类调用方法
        login.validate();
        login.login();
    }
}

输出

before validate
validate
after validate
before login
login
after login

二、原理简述

  代理对象怎么获得的呢?这得从Proxy类的newProxyInstance开始,

    public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)  throws IllegalArgumentException
    {
        if (h == null) {
            throw new NullPointerException();
        }
        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, interfaces); // stack walk magic: do not refactor

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            SecurityManager sm = System.getSecurityManager();
            if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
                // create proxy instance with doPrivilege as the proxy class may
                // implement non-public interfaces that requires a special permission
                return AccessController.doPrivileged(new PrivilegedAction<Object>() {
                    public Object run() {
                        return newInstance(cons, ih);
                    }
                });
            } else {
                return newInstance(cons, ih);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString());
        }
    }

  具体的细节可以不去看他,这里只要看红色的部分,显示获取了一个代理类,这个代理类里面有个参数是接口,正是需要代理的对象的所实现的接口,最后返回一个这个类的实例,这个类怎么得到的呢?    //弱引用hashmap key是classloader,value是Map,缓存对应类加载器的接口数组 object存放的是生成的类的弱引用

     private static Map<ClassLoader, Map<List<String>, Object>> loaderToCache= new WeakHashMap<>();
//存放生成的代理类
    private static Map<Class<?>, Void> proxyClasses = Collections.synchronizedMap(new WeakHashMap<Class<?>, Void>());

private static Class<?> getProxyClass0(ClassLoader loader , Class<?>... interfaces) { ....................此处省略一对判断 ....................此处省略得到接口名数组             //对象的接口数组           List<String> key = Arrays.asList(interfaceNames); ....................又是一堆验证           //通过上面的接口数组生成一个类 byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);   try {    proxyClass = defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);     } catch (ClassFormatError e) {    throw new IllegalArgumentException(e.toString());      }       }     // add to set of all generated proxy classes, for isProxyClass     proxyClasses.put(proxyClass, null);           }
          finally {       synchronized (cache) {           //这里的cache是Map<List<String>,Object>的对象,最上面的那个    if (proxyClass != null) {    cache.put(key, new WeakReference<Class<?>>(proxyClass));   } else {   cache.remove(key);   }     cache.notifyAll();      }     }     //返回类      return proxyClass;

  在这函数里面,先获取这个对象的接口数组,再在缓存中去搜索看看有没有这个类,具体过程就是先通过classLoader来找Map,根据这个接口链表来找存放类的弱引用,假如没有的话就重新生成,至于怎么生成这个类的字节码,太复杂了,这里就省略了。

  那么为什么这个实现了接口的代理对象调用的是login方法,而结果确实invoke被调用了呢?我们看一下生成的$Proxy1这个类,把它反编译一下就能够看到。

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

public final class $Proxy11 extends Proxy  implements Login
{
  private static Method m1;
  private static Method m4;
  private static Method m3;
  private static Method m0;
  private static Method m2;
  
  public $Proxy11(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 validate()
  {
    try
    {
      this.h.invoke(this, m4, null);
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  public final void login()
  {
    try
    {
      this.h.invoke(this, m3, null);
      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") });
      m4 = Class.forName("Login").getMethod("validate", new Class[0]);
      m3 = Class.forName("Login").getMethod("login", new Class[0]);
      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());
    }
  }
}

而Proxy类里面

    protected InvocationHandler h;

    protected Proxy(InvocationHandler h) {
        doNewInstanceCheck();
        this.h = h;
    }

  到此真正的幕后已经出来了,就是生成了一个类,然后实例化一个对象,并把InvocationHandler接口的实现类的对象作为参数传进去,那么这个对象在调用login或者validate方法时,就会调用这个InvocationHandler接口的实现类的对象的invoke方法。

原文地址:https://www.cnblogs.com/maydow/p/4850787.html