博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
底层源码分析实现Jdk动态代理
阅读量:2198 次
发布时间:2019-05-03

本文共 21414 字,大约阅读时间需要 71 分钟。

前言

java动态代理主要有2种,Jdk动态代理、Cglib动态代理,本文主要讲解Jdk动态代理的使用、运行机制、以及源码分析。当spring没有手动开启Cglib动态代理,即:<aop:aspectj-autoproxy proxy-target-class=“true”/>或@EnableAspectJAutoProxy(proxyTargetClass = true),默认使用的就是Jdk动态代理。动态代理的应用范围很广,例如:日志、事务管理、缓存等。本文将模拟@Cacheable,即缓存在动态代理中的应用进行讲解。需要注意的是,Jdk动态代理相比起cglib动态代理,Jdk动态代理的对象必须实现接口,否则将报错。我们也将带着这个问题在源码分析中寻找答案

当@Cacheable注解在方法上时

在方法执行前,将调用Jdk动态代理优先查找Redis(或其他缓存)

当缓存不存在时,执行方法,例如查询数据库
在方法执行后,再次调用Jdk动态代理,将结果缓存到Redis中

一、使用

步骤

创建接口UserService
创建接口实现类UserServiceImpl
创建Jdk动态代理JdkCacheHandler,用于增强UserServiceImpl方法前后的缓存逻辑
代码
创建接口UserService

public interface UserService {    public String getUserByName(String name);}

创建实现类UserServiceImpl

public class UserServiceImpl implements UserService {    @Override    public String getUserByName(String name) {        System.out.println("从数据库中查询到:" + name);        return name;    }}

创建Jdk动态代理JdkCacheHandler

public class JdkCacheHandler implements InvocationHandler {    // 目标类对象    private Object target;    // 获取目标类对象    public JdkCacheHandler(Object target) {        this.target = target;    }    // 创建JDK代理    public Object createJDKProxy() {        Class clazz = target.getClass();        // 创建JDK代理需要3个参数,目标类加载器、目标类接口、代理类对象(即本身)        return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);    }    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        System.out.println("查找数据库前,在缓存中查找是否存在:" + args[0]);        // 触发目标类方法        Object result = method.invoke(target, args);        System.out.printf("查找数据库后,将%s加入到缓存中\r\n", result);        return result;    }}

创建测试类

public class JdkTest {    @Test    public void test() {        UserService userService = new UserServiceImpl();        JdkCacheHandler jdkCacheHandler = new JdkCacheHandler(userService);        UserService proxy = (UserService) jdkCacheHandler.createJDKProxy();        System.out.println("==========================");        proxy.getUserByName("bugpool");        System.out.println("==========================");        System.out.println(proxy.getClass());    }}

输出

==========================查找数据库前,在缓存中查找是否存在:bugpool从数据库中查询到:bugpool查找数据库后,将bugpool加入到缓存中==========================class com.sun.proxy.$Proxy4

二、调用机制

查看$ Proxy代码

可以看到当经过Jdk动态代理以后,生产的proxy已经不再是UserService类型了,而是$Proxy4类型,想要了解其调用机制,得先获取到proxy类的代码

System.out.println(proxy.getClass());class com.sun.proxy.$Proxy4

修改JVM运行参数,添加-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true

修改JVM运行参数.png
添加JVM运行参数.png
添加JVM运行参数.png
运行test即可在com.sun.proxy查看代码,此时生产的是class,idea打开会自动反编译

在这里插入图片描述

Proxy代码.png
在上方输出中可以看到代理类是 P r o x y 4 , 至 此 获 取 到 Proxy4,至此获取到 Proxy4Proxy4的源代码,接下去分析代理类的调用机制

public final class $Proxy4 extends Proxy implements UserService {    private static Method m1;    private static Method m3;    private static Method m2;    private static Method m0;    public $Proxy4(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 String getUserByName(String var1) throws  {        try {            return (String)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("proxy.jdk.UserService").getMethod("getUserByName", 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());        }    }}

调用机制

从proxy调用开始

// JdkTest.javaproxy.getUserByName("bugpool");

proxy是 P r o x y 4 类 型 , 因 此 进 入 Proxy4类型,因此进入 Proxy4Proxy4的getUserByName方法

// $Proxy4.classpublic final class $Proxy4 extends Proxy implements UserService {    ...    // 构造器,传入JdkCacheHandler类的对象,正是下方调用的super.h属性    public $Proxy4(InvocationHandler var1) throws  {        super(var1);    }    public final String getUserByName(String var1) throws  {        try {            /**            *   调用父类的h属性的invoke方法            *   在下面的源码分析中,会发现h属性正是JdkCacheHandler类createJDKProxy方法中所传入的this            *   Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);            *   而this指代的正是JdkCacheHandler类的对象,因此最后调用的是JdkCacheHandler的invoke方法            */            return (String)super.h.invoke(this, m3, new Object[]{var1});        } catch (RuntimeException | Error var3) {            throw var3;        } catch (Throwable var4) {            throw new UndeclaredThrowableException(var4);        }    }    ...    static {        ...        m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));        m3 = Class.forName("proxy.jdk.UserService").getMethod("getUserByName", Class.forName("java.lang.String"));        m2 = Class.forName("java.lang.Object").getMethod("toString");        m0 = Class.forName("java.lang.Object").getMethod("hashCode");        ...    }}

因此h.invok实际调用的正是JdkCacheHandler类的invoke方法

// JdkCacheHandler.javapublic class JdkCacheHandler implements InvocationHandler {    ...    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        System.out.println("查找数据库前,在缓存中查找是否存在:" + args[0]);        // 触发目标类方法        Object result = method.invoke(target, args);        System.out.printf("查找数据库后,将%s加入到缓存中\r\n", result);        return result;    }}

而method.invoke(target, args)中的method = getUserByName,target = 构造函数传进来的UserServiceImpl对象,args = “bugpool”

// UserServiceImpl.javapublic class UserServiceImpl implements UserService {    @Override    public String getUserByName(String name) {        System.out.println("从数据库中查询到:" + name);        return name;    }}

三、源码分析

原理

了解完Jdk动态代理的调用机制,所有核心问题都落在了$Proxy4类的对象proxy是如何生成的上面?即下面这句代码上,这里先给出概述,有利于宏观上看源码。在开始之前先复习一下java的运行机制:1. 所有.java文件经过编译生成.class文件 2. 通过类加载器classLoad将.class中的字节码加载到JVM中 3. 运行

// 创建JDK代理public Object createJDKProxy() {    Class clazz = target.getClass();    // 创建JDK代理需要3个参数    // 目标类加载器:用于加载生成的字节码    // 目标类接口:用于生成字节码,也就是说$Proxy的生产仅仅需要接口数组就可以完成    // 代理类对象(即本身):用于回调invoke方法,实现方法的增强    return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);}

通过clazz.getInterfaces()获取到所有接口,通过接口可以生成类似以下字节码(注意以下给出的是代码),细细观察会发现其实各个接口方法生成的代码都是一样的,只有(String)super.h.invoke(this, m3, new Object[]{var1}的m3和参数有可能是不同的。所以其实想生成$Proxy字节码,只需要接口数组就已经完全足够了

public final String getUserByName(String var1) throws  {        try {            return (String)super.h.invoke(this, m3, new Object[]{var1});        } catch (RuntimeException | Error var3) {            throw var3;        } catch (Throwable var4) {            throw new UndeclaredThrowableException(var4);        }    }

此时已经获取到 P r o x y 4. c l a s s 的 字 节 码 , 但 是 此 处 的 字 节 码 还 未 加 载 到 J V M 中 , 因 此 需 要 调 用 c l a z z . g e t C l a s s L o a d e r ( ) 传 进 来 的 类 加 载 器 进 行 加 载 , 并 得 到 对 应 的 c l a s s , 也 就 是 Proxy4.class的字节码,但是此处的字节码还未加载到JVM中,因此需要调用clazz.getClassLoader()传进来的类加载器进行加载,并得到对应的class,也就是 Proxy4.classJVMclazz.getClassLoader()classProxy类

获取 P r o x y 类 的 构 造 函 数 , 该 构 造 函 数 有 一 个 重 要 的 参 数 h 通 过 反 射 调 用 Proxy类的构造函数,该构造函数有一个重要的参数h 通过反射调用 ProxyhProxy类的构造函数,cons.newInstance(new Object[]{h});构造函数的h正是传入的this,也就是JdkCacheHandler类的对象
将反射获取到的$Proxy对象放回
源码分析
从Proxy.newProxyInstance开始跟踪代码(注:①代表上方概述的步骤1)

// JdkCacheHandler.java// 创建JDK代理public Object createJDKProxy() {    Class clazz = target.getClass();    // 创建JDK代理需要3个参数,目标类加载器、目标类接口、代理类对象(即本身)    return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);}

跟踪代码newProxyInstance,这里需要注意在①②过程结束后,③④过程调用前,即Class<?> cl = getProxyClass0(loader, intfs);结束后,cl变量一直都只是class,即$Proxy4类,并未生成对应的对象,这里不要混淆类和对象

// Proxy.java// loader类加载器,interfaces目标类实现的所有接口,h即InvocationHandler类的对象public static Object newProxyInstance(ClassLoader loader,                                          Class
[] interfaces, InvocationHandler h) throws IllegalArgumentException { // 校验InvocationHandler是否为空 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); } // ③ 从生成的代理类中获取构造函数 // constructorParams = { InvocationHandler.class }; final Constructor
cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction
() { public Void run() { cons.setAccessible(true); return null; } }); } // ④ 调用构造函数,将InvocationHandler作为参数实例化代理对象 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); } }

跟踪Class<?> cl = getProxyClass0(loader, intfs);

// Proxy.javaprivate 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 // 如果在类加载器中已经存在实现了对应接口的代理类,则直接返回缓存中的代理类 // 否则,通过ProxyClassFactory新建代理类 return proxyClassCache.get(loader, interfaces);}

跟踪proxyClassCache.get(loader, interfaces);(注:Jdk动态代理对已经生成加载过的代理类进行了缓存以提高性能,缓存的相关代码不是我们关心的重点,可以跳过相关代码)本段代码我们主要关心V value = supplier.get();其中supplier本质是factory,通过new Factory(key, parameter, subKey, valuesMap)创建

// WeakCache.java//K和P就是WeakCache定义中的泛型,key是类加载器,parameter是接口类数组public V get(K key, P parameter) {        // 检查接口数组是否为空        Objects.requireNonNull(parameter);        expungeStaleEntries();        Object cacheKey = CacheKey.valueOf(key, refQueue);        // lazily install the 2nd level valuesMap for the particular cacheKey        ConcurrentMap
> valuesMap = map.get(cacheKey); if (valuesMap == null) { ConcurrentMap
> oldValuesMap = map.putIfAbsent(cacheKey, valuesMap = new ConcurrentHashMap<>()); if (oldValuesMap != null) { valuesMap = oldValuesMap; } } // create subKey and retrieve the possible Supplier
stored by that // subKey from valuesMap Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter)); //通过sub-key得到supplier,实质就是factory Supplier
supplier = valuesMap.get(subKey); Factory factory = null; while (true) { if (supplier != null) { // supplier might be a Factory or a CacheValue
instance // ①②步骤都在这里,如果supplier不为空,则直接调用get方法返回代理类 V value = supplier.get(); if (value != null) { return value; } } // else no supplier in cache // or a supplier that returned null (could be a cleared CacheValue // or a Factory that wasn't successful in installing the CacheValue) // lazily construct a Factory if (factory == null) { // 创建对应factory,此段代码在死循环中,下一次supplier.get()将会获取到代理类并退出循环 factory = new Factory(key, parameter, subKey, valuesMap); } if (supplier == null) { supplier = valuesMap.putIfAbsent(subKey, factory); if (supplier == null) { // successfully installed Factory // 赋值给supplier supplier = factory; } // else retry with winning supplier } else { if (valuesMap.replace(subKey, supplier, factory)) { // successfully replaced // cleared CacheEntry / unsuccessful Factory // with our Factory supplier = factory; } else { // retry with current supplier supplier = valuesMap.get(subKey); } } } }

跟踪V value = supplier.get();即Factory类的get方法,这里大部分的工作还是在做校验和缓存,我们只关心核心逻辑valueFactory.apply(key, parameter);其中valueFactory是上一个步骤传入的ProxyClassFactory

// Factory.java    public synchronized V get() { // serialize access        // re-check        // 再次检查,supplier是否是当前对象        Supplier
supplier = valuesMap.get(subKey); if (supplier != this) { // something changed while we were waiting: // might be that we were replaced by a CacheValue // or were removed because of failure -> // return null to signal WeakCache.get() to retry // the loop return null; } // else still us (supplier == this) // create new value V value = null; try { // valueFactory 是前序传进来的 new ProxyClassFactory() // ①②步骤,核心逻辑,调用valueFactory.apply生成对应代理类并加载 value = Objects.requireNonNull(valueFactory.apply(key, parameter)); } finally { if (value == null) { // remove us on failure valuesMap.remove(subKey, this); } } // the only path to reach here is with non-null value assert value != null; // wrap value with CacheValue (WeakReference) CacheValue
cacheValue = new CacheValue<>(value); // put into reverseMap reverseMap.put(cacheValue, Boolean.TRUE); // try replacing us with CacheValue (this should always succeed) if (!valuesMap.replace(subKey, this, cacheValue)) { throw new AssertionError("Should not reach here"); } // successfully replaced us with new CacheValue -> return the value // wrapped by it return value; }

跟踪核心逻辑

  • Jdk动态代理通过拼凑的方式拼凑出 P r o x y 的 全 类 名 : c o m . s u n . p r o x y . Proxy的全类名:com.sun.proxy. Proxycom.sun.proxy.proxy0.class
  • ③生产字节码byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces,accessFlags);可以看出Jdk动态代理需要interfaces接口数组进行生成字节码,这也是文章开头提出为什么必须实现接口的原因。同时从参数也可以看出需要生成字节码其实只需要接口数组,不需要其他信息。其实实现原理大概也可以猜出,Jdk动态代理通过遍历所有接口方法,为方法生成对应的return (String)super.h.invoke(this, m0~n, new Object[]{var1});代码
  • ④加载字节码:在③中获取到了字节码的字节数组,接下去就是调用classLoader将所有的字节码读入到JVM中
// ProxyClassFactory.javaprivate static final class ProxyClassFactory        implements BiFunction
[], 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
, 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) { } 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,则所有包都可以使用 // 当接口是非public,则生成的代理类必须和接口在与非public接口同一个包下 // 如果非public的接口均在同一个包下,则生成的代理类放在非public接口同一个包下 // 而如果非public的接口存在多个,且在不同包下,则抛出异常 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 // 如果都是公有的接口,则代理类默认放在com.sun.proxy package proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; } /* * Choose a name for the proxy class to generate. */ // 生成计数器,例如$proxy0~n long num = nextUniqueNumber.getAndIncrement(); // 代理类名,com.sun.proxy.$proxy0.class String proxyName = proxyPkg + proxyClassNamePrefix + num; /* * Generate the specified proxy class. */ // ③生成代理类字节码 byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); try { // ④使用传进来的classLoader将代理类字节码加载到JVM中 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()); } } }

转载地址:http://ckqub.baihongyu.com/

你可能感兴趣的文章
手把手用 IntelliJ IDEA 和 SBT 创建 scala 项目
查看>>
GAN 的 keras 实现
查看>>
AI 在 marketing 上的应用
查看>>
Logistic regression 为什么用 sigmoid ?
查看>>
Logistic Regression 为什么用极大似然函数
查看>>
SVM 的核函数选择和调参
查看>>
LightGBM 如何调参
查看>>
用 TensorFlow.js 在浏览器中训练神经网络
查看>>
cs230 深度学习 Lecture 2 编程作业: Logistic Regression with a Neural Network mindset
查看>>
梯度消失问题与如何选择激活函数
查看>>
为什么需要 Mini-batch 梯度下降,及 TensorFlow 应用举例
查看>>
为什么在优化算法中使用指数加权平均
查看>>
什么是 Q-learning
查看>>
用一个小游戏入门深度强化学习
查看>>
5 分钟入门 Google 最强NLP模型:BERT
查看>>
初探Java设计模式4:一文带你掌握JDK中的设计模式
查看>>
初探Java设计模式5:一文了解Spring涉及到的9种设计模式
查看>>
Java集合详解1:一文读懂ArrayList,Vector与Stack使用方法和实现原理
查看>>
Java集合详解2:一文读懂Queue和LinkedList
查看>>
Java集合详解3:一文读懂Iterator,fail-fast机制与比较器
查看>>