【spring】JDK动态代理的使用与源码分析

JDK动态代理的实现是在运行时,根据一组接口定义,使用Proxy、InvocationHandler等工具类去生成一个代理类和代理类实例。

JDK动态代理的使用

编写一个类实现InvocationHandler接口,主要负责需要增强的方法的增强逻辑。

package com.morris.spring.demo.proxy.jdk;

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

public class UserServiceInvocationHandler implements InvocationHandler {

    private Object target;

    public UserServiceInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("invoke before");
		Object result = method.invoke(target, args);
		System.out.println("invoke after");
		return result;
    }
}

代理类的生成:

package com.morris.spring.demo.proxy.jdk;


import cn.morris.spring.service.UserService;
import cn.morris.spring.service.UserServiceImpl;

import java.lang.reflect.Proxy;

/**
 * JDK动态代理的使用
 * 目标对象必须实现一个接口才能使用JDK动态代理
 */
public class JdkProxyDemo {

    public static void main(String[] args) {
        UserService userService = (UserService) Proxy.newProxyInstance(JdkProxyDemo.class.getClassLoader(), new Class<?>[]{UserService.class}, new UserServiceInvocationHandler(new UserServiceImpl()));
        userService.query("morris");
    }

}

总结:

  • 要使用JDK动态代理,目标对象需要实现一个接口。
  • JDK动态代理为什么要使用实现接口呢,为什么不像cglib使用继承呢?因为jdk动态代理生成的动态代理类会继承Proxy类,而java是单继承,无法再继承目标类。
  • 自定义的InvocationHandler要接收一个目标接口的实现类,因为在代理类中要执行目标对象的方法,所以必须传入目标对象,否则无法执行。

代理类长什么样?

如何查看动态生成的代理类?

方法一:通过系统属性sun.misc.ProxyGenerator.saveGeneratedFiles=true,设置了这个属性后,生成的代理类会保存在项目下的com.sun.proxy下,当然如果你的类不是public的,那么包名是目标类的包名。

新版本的JDK中的属性可能叫jdk.proxy.ProxyGenerator.saveGeneratedFiles,具体是什么可以查看类ProxyGenerator的常量。

系统属性的设置方式:

  • 通过JVM启动参数-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
  • 通过环境变量
  • 通过代码:System.getProperties().put(“sun.misc.ProxyGenerator.saveGeneratedFiles”,“true”);

方法二:使用JDK的API生成代理类

package com.morris.spring.demo.proxy.jdk;

import com.morris.spring.service.UserService;
import sun.misc.ProxyGenerator;

import java.io.FileOutputStream;
import java.io.IOException;

/**
 * 使用JDK的API生成代理类
 */
public class GenerateProxyClassDemo {
	public static void main(String[] args) throws IOException {
		byte[] bytes = ProxyGenerator.generateProxyClass(UserService.class.getSimpleName(), new Class[]{UserService.class});
		FileOutputStream fos = new FileOutputStream(UserService.class.getSimpleName() + ".class");
		fos.write(bytes);
		fos.flush();
		fos.close();
	}
}

方法三:使用第三方工具如arthas,这个工具可以导出内存中的所有类。

查看内存的代理类

使用arthas工具将内存中的com.sun.proxy包下的class文件dump下来,反编译后内容如下:

package com.sun.proxy;

import com.morris.spring.service.UserService;
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 UserService {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    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 void query(String var1) throws  {
        try {
            super.h.invoke(this, m3, 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 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"));
            m3 = Class.forName("com.morris.spring.service.UserService").getMethod("query", Class.forName("java.lang.String"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

从jdk动态代理生成的代理类的内容可以发现:

  • 代理类默认的包名为com.sun.proxy,后面看源码会发现这个会根据目标接口的访问修饰符来决定(如果是public,包名为com.sun.proxy,如果是protected,包名会跟目标接口包名相同,接口的访问修饰符不能为private)。
  • 代理类名为$Proxy开头,后面的数字每生成一个代理类会自增。
  • 代理类继承了Proxy类,实现了目标接口。
  • 代理类的目标方法会调用自己实现的InvocationHandler接口的invoke方法。

JDK动态代理源码解读

JDK动态代理的源码的入口为Proxy.newProxyInstance

Proxy的数据结构

private static final Class<?>[] constructorParams = { InvocationHandler.class }; // Proxy的构造方法的参数类型
private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory()); // 代理类的缓存
protected InvocationHandler h; // 存放传入的自定义的InvocationHandler,供其子类$Proxy0调用

Proxy.newProxyInstance

java.lang.reflect.Proxy#newProxyInstance

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

		// 获得构造参数是InvocationHandler的构造方法
        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;
                }
            });
        }
        // 调用构造方法$Proxy0(InvocationHandler h)生成代理对象实例 
        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);
    }
}

源码中主要有两步:

  1. 创建代理类。
  2. 创建代理类的实例。

而getProxyClass0()的代码如下

private static Class<?> getProxyClass0(ClassLoader loader,
                                       Class<?>... interfaces) {
    if (interfaces.length > 65535) {
        throw new IllegalArgumentException("interface limit exceeded");
    }

    // If the proxy class defined by the given loader implementing
    // the given interfaces exists, this will simply return the cached copy;
    // otherwise, it will create the proxy class via the ProxyClassFactory
	// 从静态属性proxyClassCache中获取,不存在则会调用ProxyClassFactory生成
    return proxyClassCache.get(loader, interfaces);
}

ProxyClassFactory

ProxyClassFactory是一个BiFunction,用于生成代理类

private static final class ProxyClassFactory implements BiFunction<ClassLoader, Class<?>[], Class<?>> {
    // prefix for all proxy class names
    private static final String proxyClassNamePrefix = "$Proxy"; // 生成的代理类的名称前缀

    // next number to use for generation of unique proxy class names
    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) {
            /*
             * Verify that the class loader resolves the name of this
             * interface to the same Class object.
             */
            Class<?> interfaceClass = null;
            try {
                interfaceClass = Class.forName(intf.getName(), false, loader);
            } catch (ClassNotFoundException e) {
            }
		    // 判断传入的接口是不是传入的classloader加载的
		    // 对象的比较在同一个classloader下才有意义
            if (interfaceClass != intf) {
                throw new IllegalArgumentException(
                    intf + " is not visible from class loader");
            }
            /*
             * Verify that the Class object actually represents an
             * interface.
             */
			// 传入的是不是一个接口,只能代理接口
            if (!interfaceClass.isInterface()) {
                throw new IllegalArgumentException(
                    interfaceClass.getName() + " is not an interface");
            }
            /*
             * Verify that this interface is not a duplicate.
             */
            if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                throw new IllegalArgumentException(
                    "repeated interface: " + interfaceClass.getName());
            }
        }

        String proxyPkg = null;     // package to define proxy class in
        int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

        /*
         * Record the package of a non-public proxy interface so that the
         * proxy class will be defined in the same package.  Verify that
         * all non-public proxy interfaces are in the same package.
         */
	    // 判断接口的访问修饰符,public的接口,生成的类的包名可以随意,但是package的接口生成的代理类的包名需与接口一致
        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) {
            // if no non-public proxy interfaces, use com.sun.proxy package
            proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
        }

        /*
         * Choose a name for the proxy class to generate.
         */
        long num = nextUniqueNumber.getAndIncrement();
        String proxyName = proxyPkg + proxyClassNamePrefix + num;

        /*
         * Generate the specified proxy class.
         */
		// 生成代理类.class数组 此处是直接生成字节码
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
            proxyName, interfaces, accessFlags);
        try {
            // 加载代理类,此处调用的是一个本地方法
            return defineClass0(loader, proxyName,
                                proxyClassFile, 0, proxyClassFile.length);
        } catch (ClassFormatError e) {
            /*
             * A ClassFormatError here means that (barring bugs in the
             * proxy class generation code) there was some other
             * invalid aspect of the arguments supplied to the proxy
             * class creation (such as virtual machine limitations
             * exceeded).
             */
            throw new IllegalArgumentException(e.toString());
        }
    }
}

从源码可以看出:

  • jdk动态代理生成的代理类继承了Proxy类。
  • 底层是直接生成.class字节码数组,内存操作,速度快。
  • 底层使用了WeakCache做缓存。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

morris131

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值