Java动态代理(二)

       在Java动态代理(一),我们了解了代理模式的含义,也用程序演示了静态代理和动态代理的实现。现在,我们就来学习一下java动态代理的源码。

代理类的字节码查看

       我们知道,jdk动态代理是在程序运行的时候动态生成代理类,那么生成的代理类里面是什么样的了。我们可以通过下面的方式获得生成的代理类的字节码,然后通过反编译查看代理类的内容。

package com.code.proxy;

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

/**
 * 获取动态代理类的字节码
 */
public class DymaticProxyCode {

	public static void main(String[] args) throws Exception {
		// 1.设置saveGeneratedFiles值为true则生成 class字节码文件方便分析
		System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
		// 2.获取动态代理类
		Class<?> proxyClazz = Proxy.getProxyClass(Star.class.getClassLoader(), Star.class);
		// 3.获得代理类的构造函数,并传入参数类型InvocationHandler.class
		Constructor<?> constructor = proxyClazz.getConstructor(InvocationHandler.class);
		// 4.通过构造函数来创建动态代理对象,将自定义的InvocationHandler实例传入
		StarProxyHandler handler = new StarProxyHandler();
		handler.bind(new LiuDehua());
		Star star = (Star) constructor.newInstance(handler);
		// 5.通过代理对象调用目标方法
		star.sing();
	}

}

        由于动态代理类的默认包路径是com.sun.proxy,为了存储字节码,我们需要在项目根路径新建目录com/sun/proxy,否则程序会报错。

        通过反编译,我们先来看一下动态代理类的内容:

package com.sun.proxy;

import com.code.proxy.Star;
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 Star
{
  private static Method m1;
  private static Method m4;
  private static Method m3;
  private static Method m0;
  private static Method m2;

  public Proxy0()
    throws 
  {
    super(paramInvocationHandler);
  }

  public final boolean equals()
    throws 
  {
    try
    {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final void dance()
    throws 
  {
    try
    {
      this.h.invoke(this, m4, null);
      return;
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final void sing()
    throws 
  {
    try
    {
      this.h.invoke(this, m3, null);
      return;
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final int hashCode()
    throws 
  {
    try
    {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final String toString()
    throws 
  {
    try
    {
      return ((String)this.h.invoke(this, m2, null));
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    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("com.code.proxy.Star").getMethod("dance", new Class[0]);
      m3 = Class.forName("com.code.proxy.Star").getMethod("sing", 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());
    }
  }
}

        我们看到,代理类里面主要包括以下内容:

  • 代理类的构造函数会传递一个InvocationHandler对象
  • 代理类里面会实现接口的sing、dance方法,另外还有equals、hashCode、toString方法(这三个方法其实是继承Object,每个代理类都会实现)
  • 代理类里面方法的实现,最终都会调用this.h.invoke,也就是说对代理对象的调用最终实际调用的是InvocationHandler对象
  • 代理类继承Proxy,实现Star接口(目标接口)

Proxy源码解析

1.    首先,从Proxy.getProxyClass(Star.class.getClassLoader(), Star.class)进入查看。

    public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces){
        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        return getProxyClass0(loader, intfs);
    }
private static Class<?> getProxyClass0(ClassLoader loader,Class<?>... interfaces) {
    //接口数目不能大于65535
    if (interfaces.length > 65535) {
        throw new IllegalArgumentException("interface limit exceeded");
    }
    //首先,会从缓存获取代理类。如果缓存里面没有,则会通过ProxyClassFactory创建
    return proxyClassCache.get(loader, interfaces);
}

2.    proxyClassCache.get(loader, interfaces)里面主要是根据不同的classLoader从不同的缓存里面获取代理类的supplier,最后从supplier里面获取代理类

3.    supplier属于ProxyClassFactory类,最终会调用ProxyClassFactory.apply

//动态代理工厂类
private static final class ProxyClassFactory
	implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
	// 代理类的类名以$Proxy作为前缀
	private static final String proxyClassNamePrefix = "$Proxy";
	
	// 为保证代理类类名唯一的一个自增序号
	private static final AtomicLong nextUniqueNumber = new AtomicLong();
	
	@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");
	        }
	        //判断需要代理的接口类是一个interface
	        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;
	
	    //如果接口不是public类型,则需要校验所有接口的包路径是否一致
	    for (Class<?> intf : interfaces) {
	        int flags = intf.getModifiers();
	        //判断接口是否是public
	        if (!Modifier.isPublic(flags)) {
	            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) {
	    	//如果所有的接口都是public,或者都没指定包路径,则以默认的com.sun.proxy作为代理类的包路径
	        proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
	    }
	
	    //生成代理类类名的序号
	    long num = nextUniqueNumber.getAndIncrement();
	    //代理类包含路径的全类名,eg:com.sun.proxy.$Proxy0
	    String proxyName = proxyPkg + proxyClassNamePrefix + num;
	
	    //生成代理类的class文件
	    byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
	        proxyName, interfaces);
	    try {
	    	//调用native方法加载代理类的class文件,并返回代理类对象
	        return defineClass0(loader, proxyName,
	                            proxyClassFile, 0, proxyClassFile.length);
	    } catch (ClassFormatError e) {
	        throw new IllegalArgumentException(e.toString());
	    }
	}
}
4.    从上面的代码可以看到,代理类的class文件的生成逻辑在 ProxyGenerator.generateProxyClass里面。但是,ProxyGenerator在jdk的sun包路径下,而sun路径下的代码并没有开源。我们可以通过反编译或者从openJdk获取这一部分的源码
private byte[] generateClassFile() {
    //添加hashCode方法
    addProxyMethod(hashCodeMethod, Object.class);
    //添加equals方法
    addProxyMethod(equalsMethod, Object.class);
    //添加toString方法
    addProxyMethod(toStringMethod, Object.class);

    //从接口获取所有的方法
    for (Class<?> intf : interfaces) {
        for (Method m : intf.getMethods()) {
            addProxyMethod(m, intf);
        }
    }

    //判断同一签名的所有方法的返回类型是否兼容的
    //签名 : 方法名+参数
    for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
        checkReturnTypes(sigmethods);
    }

    try {
    	//添加构造方法
        methods.add(generateConstructor());

        for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
            for (ProxyMethod pm : sigmethods) {

                // 添加方法的参数
                fields.add(new FieldInfo(pm.methodFieldName,
                    "Ljava/lang/reflect/Method;",
                     ACC_PRIVATE | ACC_STATIC));

                // 添加代理类的方法
                methods.add(pm.generateMethod());
            }
        }
        //添加静态初始化方法 eg:
       /* static
        {
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
            m4 = Class.forName("com.code.proxy.Star").getMethod("dance", new Class[0]);
        }*/
        methods.add(generateStaticInitializer());

    } catch (IOException e) {
        throw new InternalError("unexpected I/O Exception");
    }
    //代理类的方法个数不能超过65535
    if (methods.size() > 65535) {
        throw new IllegalArgumentException("method limit exceeded");
    }
    //代理类的参数个数不能超过65535
    if (fields.size() > 65535) {
        throw new IllegalArgumentException("field limit exceeded");
    }

    //生成最终的class文件
    ...

    return bout.toByteArray();
}
5.    代理类生成之后,我们就可以获取代理类的构造方法,然后通过构造方法传递目标对象,最终得到代理对象
//得到代理类的构造函数
Constructor<?> constructor = proxyClazz.getConstructor(InvocationHandler.class);
//实例化代理对象
Star star = (Star) constructor.newInstance(handler);

动态代理实现过程总结

1.    对被代理类做一些校验,如是否为接口、是否属于同一包路径、同一签名的返回类型是否可兼容等

2.    获取目标接口的所有方法和属性,并添加equals、hashCode、toString三个默认方法

3.    根据上述步骤获取的代理类的结构,生成class文件的二进制文件

4.    通过native方法将上述代理类的二进制文件装载到jvm


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值