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

心已赠人 2022-11-06 11:52 232阅读 0赞

JDK动态代理的使用

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

使用

编写一个类实现InvocationHandler接口。

  1. package com.morris.spring.proxy.dynamic.jdk;
  2. import java.lang.reflect.InvocationHandler;
  3. import java.lang.reflect.Method;
  4. public class UserServiceInvocationHandler implements InvocationHandler {
  5. private Object tartget;
  6. public UserServiceInvocationHandler(Object tartget) {
  7. this.tartget = tartget;
  8. }
  9. @Override
  10. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  11. System.out.println("invoke before");
  12. Object result = method.invoke(tartget, args);
  13. System.out.println("invoke after");
  14. return result;
  15. }
  16. }

测试类

  1. package com.morris.spring.proxy.dynamic.jdk;
  2. import com.morris.spring.service.UserService;
  3. import com.morris.spring.service.UserServiceImpl;
  4. import java.lang.reflect.Proxy;
  5. public class JdkProxyDemo {
  6. public static void main(String[] args) {
  7. UserService userService = (UserService) Proxy.newProxyInstance(JdkProxyDemo.class.getClassLoader(), new Class<?>[]{ UserService.class}, new UserServiceInvocationHandler(new UserServiceImpl()));
  8. userService.query();
  9. }
  10. }

总结:

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

代理类长什么样?

使用arthas工具将内存中的com.sun.proxy包下的class文件dump下来,反编译后内容如下,内容已去除toString等不重要方法和属性。

  1. package com.sun.proxy;
  2. import com.morris.spring.proxy.IUserService;
  3. import java.lang.reflect.InvocationHandler;
  4. import java.lang.reflect.Method;
  5. import java.lang.reflect.Proxy;
  6. import java.lang.reflect.UndeclaredThrowableException;
  7. public final class $Proxy0 extends Proxy implements IUserService {
  8. private static Method m3;
  9. public $Proxy0(InvocationHandler paramInvocationHandler) {
  10. super(paramInvocationHandler);
  11. }
  12. static {
  13. try {
  14. m3 = Class.forName("com.morris.spring.proxy.IUserService").getMethod("query", new Class[0]);
  15. } catch (NoSuchMethodException localNoSuchMethodException) {
  16. throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
  17. } catch (ClassNotFoundException localClassNotFoundException) {
  18. throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
  19. }
  20. }
  21. public final String query() {
  22. try {
  23. return (String) this.h.invoke(this, m3, null);
  24. } catch (Error | RuntimeException localError) {
  25. throw localError;
  26. } catch (Throwable localThrowable) {
  27. throw new UndeclaredThrowableException(localThrowable);
  28. }
  29. }
  30. }

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

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

JDK动态代理源码解读

在这里插入图片描述

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

Proxy的数据结构

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

Proxy.newProxyInstance

java.lang.reflect.Proxy#newProxyInstance

  1. public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException {
  2. Objects.requireNonNull(h);
  3. final Class<?>[] intfs = interfaces.clone();
  4. final SecurityManager sm = System.getSecurityManager();
  5. if (sm != null) {
  6. checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
  7. }
  8. /* * Look up or generate the designated proxy class. */
  9. // 重点在这,获得代理对象
  10. Class<?> cl = getProxyClass0(loader, intfs);
  11. /* * Invoke its constructor with the designated invocation handler. */
  12. try {
  13. if (sm != null) {
  14. checkNewProxyPermission(Reflection.getCallerClass(), cl);
  15. }
  16. // 获得构造参数是InvocationHandler的构造方法
  17. final Constructor<?> cons = cl.getConstructor(constructorParams);
  18. final InvocationHandler ih = h;
  19. if (!Modifier.isPublic(cl.getModifiers())) {
  20. AccessController.doPrivileged(new PrivilegedAction<Void>() {
  21. public Void run() {
  22. cons.setAccessible(true);
  23. return null;
  24. }
  25. });
  26. }
  27. // 调用构造方法$Proxy0(InvocationHandler h)生成代理对象实例
  28. return cons.newInstance(new Object[]{ h});
  29. } catch (IllegalAccessException|InstantiationException e) {
  30. throw new InternalError(e.toString(), e);
  31. } catch (InvocationTargetException e) {
  32. Throwable t = e.getCause();
  33. if (t instanceof RuntimeException) {
  34. throw (RuntimeException) t;
  35. } else {
  36. throw new InternalError(t.toString(), t);
  37. }
  38. } catch (NoSuchMethodException e) {
  39. throw new InternalError(e.toString(), e);
  40. }
  41. }

源码中主要有两步:

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

而getProxyClass0()的代码如下

  1. private static Class<?> getProxyClass0(ClassLoader loader,
  2. Class<?>... interfaces) {
  3. if (interfaces.length > 65535) {
  4. throw new IllegalArgumentException("interface limit exceeded");
  5. }
  6. // If the proxy class defined by the given loader implementing
  7. // the given interfaces exists, this will simply return the cached copy;
  8. // otherwise, it will create the proxy class via the ProxyClassFactory
  9. // 从静态属性proxyClassCache中获取,不存在则会调用ProxyClassFactory生成
  10. return proxyClassCache.get(loader, interfaces);
  11. }

ProxyClassFactory

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

  1. private static final class ProxyClassFactory implements BiFunction<ClassLoader, Class<?>[], Class<?>> {
  2. // prefix for all proxy class names
  3. private static final String proxyClassNamePrefix = "$Proxy"; // 生成的代理类的名称前缀
  4. // next number to use for generation of unique proxy class names
  5. private static final AtomicLong nextUniqueNumber = new AtomicLong(); // 计数器
  6. @Override
  7. public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
  8. Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
  9. for (Class<?> intf : interfaces) {
  10. /* * Verify that the class loader resolves the name of this * interface to the same Class object. */
  11. Class<?> interfaceClass = null;
  12. try {
  13. interfaceClass = Class.forName(intf.getName(), false, loader);
  14. } catch (ClassNotFoundException e) {
  15. }
  16. // 判断传入的接口是不是传入的classloader加载的
  17. // 对象的比较在同一个classloader下才有意义
  18. if (interfaceClass != intf) {
  19. throw new IllegalArgumentException(
  20. intf + " is not visible from class loader");
  21. }
  22. /* * Verify that the Class object actually represents an * interface. */
  23. // 传入的是不是一个接口,只能代理接口
  24. if (!interfaceClass.isInterface()) {
  25. throw new IllegalArgumentException(
  26. interfaceClass.getName() + " is not an interface");
  27. }
  28. /* * Verify that this interface is not a duplicate. */
  29. if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
  30. throw new IllegalArgumentException(
  31. "repeated interface: " + interfaceClass.getName());
  32. }
  33. }
  34. String proxyPkg = null; // package to define proxy class in
  35. int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
  36. /* * 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. */
  37. // 判断接口的访问修饰符,public的接口,生成的类的包名可以随意,但是package的接口生成的代理类的包名需与接口一致
  38. for (Class<?> intf : interfaces) {
  39. int flags = intf.getModifiers();
  40. if (!Modifier.isPublic(flags)) {
  41. accessFlags = Modifier.FINAL;
  42. String name = intf.getName();
  43. int n = name.lastIndexOf('.');
  44. String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
  45. if (proxyPkg == null) {
  46. proxyPkg = pkg;
  47. } else if (!pkg.equals(proxyPkg)) {
  48. throw new IllegalArgumentException(
  49. "non-public interfaces from different packages");
  50. }
  51. }
  52. }
  53. if (proxyPkg == null) {
  54. // if no non-public proxy interfaces, use com.sun.proxy package
  55. proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
  56. }
  57. /* * Choose a name for the proxy class to generate. */
  58. long num = nextUniqueNumber.getAndIncrement();
  59. String proxyName = proxyPkg + proxyClassNamePrefix + num;
  60. /* * Generate the specified proxy class. */
  61. // 生成代理类.class数组 此处是直接生成字节码
  62. byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
  63. proxyName, interfaces, accessFlags);
  64. try {
  65. // 加载代理类,此处调用的是一个本地方法
  66. return defineClass0(loader, proxyName,
  67. proxyClassFile, 0, proxyClassFile.length);
  68. } catch (ClassFormatError e) {
  69. /* * 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). */
  70. throw new IllegalArgumentException(e.toString());
  71. }
  72. }
  73. }

从源码可以看出:

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

发表评论

表情:
评论列表 (有 0 条评论,232人围观)

还没有评论,来说两句吧...

相关阅读

    相关 分析JDK动态代理

    引言 动态代理非常的重要,虽然我们在日常的工作中没有非常底层的 编写过动态代理的代码,但是动态代理却起着非常重要的功能,想一下我们经常使用的框架: 日志框架、AOP等等,

    相关 JDK动态代理分析

    一、代理模式是什么? 代理模式就是给一个对象提供一个代理对象,并由代理对象管理着被代理对象的引用。就像生活中的代理律师,你只需要找好代理律师,剩下的都交给代理律师来打理。