参考:JDK动态代理-超详细源码分析 - 简书 (jianshu.com)
文章目录- 动态代理及JDK代理源码解析
- 一、为什么需要动态代理
- 什么是代理模式?
- 静态代理:
- 实现步骤:
- 实现代码:
- 分析:
- 动态代理:
- JDK动态代理
- 实现步骤:
- 实现代码:
- CGLIB动态代理:
- 实现步骤:
- 二、动态代理对象是如何产生的?
- java.lang.reflect.Proxy.java
- newInstance method
- getProxyClass0 method
- java.lang.reflect.WeakCache.java
- get
- factory.get
- java.lang.reflect.Proxy.java
- 阅读源码断点:
- 再次引用了:
代理模式就是给某个对象(后称为初始对象)创建一个代理对象,其他对象可以通过代理对象(简称代理)来控制初始对象。
初始对象的代码并不会改变,但是因为有代理对象的存在,可以在执行初始对象的执行过程之外再加一些操作,比如说:执行初始对象向数据库添加了一条数据,然后又通过代理对象删除了这条数据,这个例子比较难理解,因为肯定会问这样的意义是什么?
这里写者主要向强调的是代理是可以操作初始对象的参数和结果,可以通过增加更多的操作达到更多的目的,而不改变原来的代码,至于这个目的是增强还是削弱,这本身就是不同情景下不同的定义的问题,这里只是从客观的角度去看。
说回来,一个更恰当的例子我们可以在执行初始对象删除一条数据,并在这个操作的前后分别打印“我要删除了!”和“我已经删除了!”
静态代理:实现步骤:有动态代理那么一定就会有静态代理,那么什么是静态代理呢?
静态代理和动态代理的主要区别就在于:在程序运行前是否已经存在编译好的代理类
这句话是不是很绕口,这里可以先进行预先解释一下:
对于被代理类来说如果要加强它的操作常见的方式就是再创建一个类(也就是代理类)持有这个被代理类的对象,然后将增强的操作逻辑放入其中,那么这个就需要实现编写这个类的代码,那么如果每个被代理类都需要编写一个代理类的代码,这个情况下,自然而然就会出现一个需求:一次编写,到处运行,针对某种需求统一编写一个代理类,使得这个代理类可以应用于多个被代理对象,而这种需不需要为每个类编写代理类的差别就是动态代理和静态代理的之间的区别。
还是没有理解,接着向下看:
静态代理就是在程序运行前就已经存在了编译好的代理类
- 定义业务接口
- impl(被代理类)实现业务接口
- 定义代理类也实现业务接口
- 最后调用代理类实现增强
业务接口:
public interface IUserService {
public void select();
}
业务接口实现类:
public class UserServiceImpl implements IUserService{
@Override
public void select() {
System.out.println("method invoke in impl!");
}
代理类:
public class IUserServiceProxy implements IUserService{
private IUserService iUserService;
public IUserServiceProxy(IUserService iUserService) {
this.iUserService = iUserService;
}
@Override
public void select() {
System.out.println("方法执行前加强");
iUserService.select();
System.out.println("方法执行后加强");
}
}
服务调用对象:
public class StaticProxyTest {
public static void main(String[] args) {
IUserService target = new UserServiceImpl();
IUserService proxy = new IUserServiceProxy(target);
proxy.select();
}
分析:
这样也能实现代理的功能但是静态代理有一些问题的缺点的存在:
- 代理类和被代理类实现了相同的接口,这样会导致代码重复,如果接口增加一个方法,那么除了被代理类要实现这个方法,代理类也要实现这个方法
- 这个代理类只能服务一个对象,或者一个类型的对象,并不能广泛复用,如果需要给其他对象也增强就需要创建更多的代理类
JDK动态代理 实现步骤:那么因为静态代理有这样那样的缺点,那么就需要一种新的方法来解决这样的一些问题,这里很显然的就可以考虑到在代码运行的过程中根据不同的对象相应的创建不同适配的代理类,也就是动态代理:
在程序运行期间根据需要动态创建代理类及其实例来完成具体的功能,常用动态代理的方式为:
- JDK动态代理
- CGLIB动态代理
- 创建被代理的类和接口
- 创建InvocationHandler接口的实现类,在invoke方法中实现代理逻辑
- 通过Proxy 的静态方法newProxyInstance创建代理对象
- 使用代理对象
代理的接口和类-> 复用上面的IUserService 和 UserServiceImpl
InvocationHandler 接口的实现类:
public class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("执行前增强!");
Object returnValues = method.invoke(target,args);
System.out.println("执行后增强!");
return returnValues;
}
}
创建代理对象并使用代理对象:
public class JdkProxyTest {
public static void main(String[] args) {
IUserService target = new UserServiceImpl();
MyInvocationHandler handler = new MyInvocationHandler(target);
IUserService proxy = (IUserService) Proxy.newProxyInstance(
JdkProxyTest.class.getClassLoader(),
target.getClass().getInterfaces(),
handler);
proxy.select();
}
}
这里我们并没有为每一个被代理的类去创建一个对应的代理类,而是动态的根据代码去创建了代理对象(毕竟你自己写代理类的目的其实也是为了获取代理对象)
CGLIB动态代理:实现步骤:这里有一个区分就是:
JDK代理是基于UserService -UserServiceImpl 这种实现接口的模式进行的带来,而被代理的类是UserServiceImpl 对象
这时动态代理的对象必须实现一个或多个接口,那么自然而然就会有一个需求:如何代理没有实现接口的类?为什么JDK代理的类必须要实现接口才能代理?我们分为两部分来说明白,而第二个问题放到第二个标题JDK动态代理是怎样产生的方面去说明。
这个情况下就可以通过CGLIB动态代理来解决第一个问题:
CGLIB:Code Generation Libary 是一个高性能代码生成包,采用非常底层的字节码技术,针对目标类生成一个子类,并对子类进行增强
- 创建被代理的类和对象
- 创建实现了MethodInterceptor 的代理类
- 封装代理逻辑
- 客户端调用
被代理的类:UserDao
public class UserDao {
public void addUser() {
System.out.println("Add a user instance!");
}
public void deleteUser() {
System.out.println("Delete a user instance!");
}
}
切面类:UserDaoAspect
切面类本质上是增强逻辑的具体实现,而被代理类封装的是增强逻辑的执行顺序
public class UserDaoAspect {
public void invoke_before(){
System.out.println("invokeBefore has completed;");
}
public void invoke_after(){
System.out.println("invokeBefore has completed;");
}
}
代理类:CglibProxy
public class CglibProxy implements MethodInterceptor {
public Object creatProxy(Object o){
Enhancer enhancer =new Enhancer();
enhancer.setSuperclass(o.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
UserDaoAspect userDaoAspect = new UserDaoAspect();
userDaoAspect.invoke_before();
Object returnResult = methodProxy.invokeSuper(o,args);
userDaoAspect.invoke_after();
return returnResult;
}
}
Cglib 测试类:CglibTest:
public class CglibTest {
public static void main(String[] args) {
CglibProxy cglibProxy = new CglibProxy();
UserDao userDao = new UserDao();
UserDao userDaoExtend = (UserDao) cglibProxy.creatProxy(userDao);
userDaoExtend.addUser();
userDaoExtend.deleteUser();
}
}
实现效果:
二、动态代理对象是如何产生的?这部分就比较复杂了,初学的小伙伴们可能需要费一些力气了
这里插入一个主题:部署JDK源码环境:可以直接使用我的jdk源码阅读项目
,具体如何在idea 里面配置jdk阅读环境 这里就不赘述了,大家可以直接把我的项目拉下来使用:
OldClassmatesWang/Java_Source_Learn: 本地jdk1.8源码阅读项目 (github.com)
接下来就是源码的阅读了:这里看的是JDK动态代理实现的源码:
java.lang.reflect.Proxy.java newInstance method首先查看:Proxy.newInstance(classloader,interfaces,invocationHandler)
@CallerSensitive
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);
}
Class> cl = getProxyClass0(loader, intfs);
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
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;
}
});
}
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);
-
-
整体功能:
- 整体功能就是通过上面的这个代码获取到动态生成的代理类的类对象,然后通过类对象获取它的构造函数,再通过构造函数创建实例
-
问题:
- 这里虽然大概看出了实例是通过生成的代理类的类对象产生的,但是实际上其实还是没有说明,代理类的类对象是如何产生的
-
问题转换:
- 现在问题转换为:动态生成的代理类的类对象是如何产生的?
-
传入参数:
- 1)loader 指定的类加载器
- 2)被代理类实现的接口的类对象
-
下一个要查看的方法:
-
java.lang.reflect.Proxy.java
-
private static Class> getProxyClass0(ClassLoader loader, Class>... interfaces)
-
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
return proxyClassCache.get(loader, interfaces);
}
proxyClassCache是什么?这里就比较复杂了
首先先看他的定义:(后面的一些内容因为我本身理解有限,就引用一部分参考博客的原文了)
private static final WeakCache[], Class>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
是一个WeakCache 的对象,它刚刚调用的get方法是WeakCache 里的方法:
//K代表key的类型,P代表参数的类型,V代表value的类型。 // WeakCache[], Class>> proxyClassCache 说明proxyClassCache存的值是Class>对象,正是我们需要的代理类对象。 final class WeakCache { private final ReferenceQueue refQueue = new ReferenceQueue<>(); // the key type is Object for supporting null key private final ConcurrentMap
其中map变量是实现缓存的核心变量,他是一个双重的Map结构: (key, sub-key) -> value。
其中key是传进来的Classloader进行包装后的对象,
sub-key是由WeakCache构造函数传人的KeyFactory()生成的。
value就是产生代理类的对象,是由WeakCache构造函数传人的ProxyClassFactory()生成的。
产生sub-key的KeyFactory代码如下,这个我们不去深究,只要知道他是根据传入的ClassLoader和接口类生成sub-key即可。
private static final class KeyFactory
implements BiFunction[], Object>
{
@Override
public Object apply(ClassLoader classLoader, Class>[] interfaces) {
switch (interfaces.length) {
case 1: return new Key1(interfaces[0]); // the most frequent
case 2: return new Key2(interfaces[0], interfaces[1]);
case 0: return key0;
default: return new KeyX(interfaces);
}
}
}
通过sub-key拿到一个Supplier
- 整体功能:
- 大体上这就是WeakCache类的作用:被代理类的类加载器和实现的类的接口(转换成一个Key对象)会作为key存在map里面,如果之前这个被加载过,则可以通过这种方式用key来获取对应已经生成好的,还在java缓存中的类的Class 对象
- 这里我们明显是第一次调用,所以我们肯定是第一次生成的,所以接下来的关键还是看如何生成这个代理类的Class对象
- 下一个要查看的方法:
- java.lang.reflect.WeakCache.java
- public V get(K key, P 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
-
整体功能:
- 这部分的代码主要就是将factory 创建并放到缓存当中
-
问题:
-
其实还没有写明是怎么生成的
-
核心代码:
-
factory = new Factory(key, parameter, subKey, valuesMap);
-
-
-
问题转换:
- 现在问题转换为:factory是如何生成被代理对象的
-
传入参数:
- key:classloader
- parameter:interfaces 实现的类
- subkey:根据interfaces 生成的,和key一起组成第二层的key
- valuemap:虚拟机中存储这写被代理类的hashmap
-
下一个要查看的方法:
- factory.get 这是WeakCache 的内部类方法
public synchronized V get() { // serialize access
// re-check
Supplier supplier = valuesMap.get(subKey);
//重新检查得到的supplier是不是当前对象
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生成的
//valueFactory就是我们传入的 new ProxyClassFactory()
//一会我们分析ProxyClassFactory()的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)
//把value包装成弱引用
Cachevalue cachevalue = new Cachevalue<>(value);
// put into reverseMap
// 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;
}
}
核心代码:
value = Objects.requireNonNull(valueFactory.apply(key, parameter));java.lang.reflect.Proxy.java
找到了valueFactory.apply 的实现点,是Proxy 的内部类ProxyClassFactory实现的。
/这里的BiFunction是个函数式接口,可以理解为用T,U两种类型做参数,得到R类型的返回值 private 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) { 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"); } 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; // package to define proxy class in //代理类访问控制符: public ,final int accessFlags = Modifier.PUBLIC | Modifier.FINAL; //验证所有非公共的接口在同一个包内;公共的就无需处理 //生成包名和类名的逻辑,包名默认是com.sun.proxy,类名默认是$Proxy 加上一个自增的整数值 //如果被代理类是 non-public proxy interface ,则用和被代理类接口一样的包名 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 + "."; } long num = nextUniqueNumber.getAndIncrement(); //代理类的完全限定名,如com.sun.proxy.$Proxy0.calss String proxyName = proxyPkg + proxyClassNamePrefix + num; //核心部分,生成代理类的字节码 byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); try { //把代理类加载到JVM中,至此动态代理过程基本结束了 return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { throw new IllegalArgumentException(e.toString()); } } }
-
整体功能:
-
获取预计实现代理类的属性包括包名,包括验证访问修饰符等等,然后根据这些属性生成了对应的代理类的字节码,然后将这些字节码加载到java虚拟机当中,由此完成了动态代理
-
核心代码:
-
byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); -
再往下看就是如何生成这个字节码了,就超出本次分析的内容了
-
-
到此为止其实阅读源码的内容已经明白了,JDK底层如何实现动态代理的过程其实我们也明白了,可以明确的几点就是:
- 动态代理对于UserDaoImpl 会生成对应的代理类的Class对象,然后调用时会实例化这个Class对象,然后生成对应的代理类的实例
- 为什么这个类一定要实现一个接口
- 其实就是这里的key0的问题
- key0这个过程其实没有理解很透彻,希望大家和我来交流,我初步的理解就是因为key0不是弱引用(我这里就理解成有个类没有实现),所以代码中进行了限定无法将对应生成的Proxy存储到缓存中
- 其实就是这里的key0的问题
到这里其实已经分析完了,但是本着深究的态度,决定看看JDK生成的动态代理字节码是什么,于是我们将字节码保存到磁盘上的class文件中。代码如下:
package com.zhb.jdk.proxy; import java.io.FileOutputStream; import java.io.IOException; import java.lang.reflect.Proxy; import com.zhb.jdk.dynamicProxy.HelloworldImpl; import sun.misc.ProxyGenerator; public class DynamicProxyTest { public static void main(String[] args) { IUserService target = new UserServiceImpl(); MyInvocationHandler handler = new MyInvocationHandler(target); //第一个参数是指定代理类的类加载器(我们传入当前测试类的类加载器) //第二个参数是代理类需要实现的接口(我们传入被代理类实现的接口,这样生成的代理类和被代理类就实现了相同的接口) //第三个参数是invocation handler,用来处理方法的调用。这里传入我们自己实现的handler IUserService proxyObject = (IUserService) Proxy.newProxyInstance(DynamicProxyTest.class.getClassLoader(), target.getClass().getInterfaces(), handler); proxyObject.add("陈粒"); String path = "D:/$Proxy0.class"; byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", HelloworldImpl.class.getInterfaces()); FileOutputStream out = null; try { out = new FileOutputStream(path); out.write(classFile); out.flush(); } catch (Exception e) { e.printStackTrace(); } finally { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } }运行这段代码,会在D盘生成一个名为$Proxy0.class的文件。通过反编译工具,得到JDK为我们生成的代理类是这样的:
// Decompiled by Jad v1.5.8e2. Copyright 2001 Pavel Kouznetsov. // Jad home page: http://kpdus.tripod.com/jad.html // Decompiler options: packimports(3) fieldsfirst ansi space import com.zhb.jdk.proxy.IUserService; import java.lang.reflect.*; public final class $Proxy0 extends Proxy implements IUserService { private static Method m1; private static Method m2; private static Method m3; private static Method m0; //代理类的构造函数,其参数正是是InvocationHandler实例, //Proxy.newInstance方法就是通过通过这个构造函数来创建代理实例的 public $Proxy0(InvocationHandler invocationhandler) { super(invocationhandler); } // Object类中的三个方法,equals,toString, hashCode public final boolean equals(Object obj) { try { return ((Boolean)super.h.invoke(this, m1, new Object[] { obj })).booleanValue(); } catch (Error ) { } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } public final String toString() { try { return (String)super.h.invoke(this, m2, null); } catch (Error ) { } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } //接口代理方法 public final void add(String s) { try { // invocation handler的 invoke方法在这里被调用,也就是封装的代理逻辑 super.h.invoke(this, m3, new Object[] { s }); return; } catch (Error ) { } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } public final int hashCode() { try { // 在这里调用了invoke方法。 return ((Integer)super.h.invoke(this, m0, null)).intValue(); } catch (Error ) { } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } // 静态代码块对变量进行一些初始化工作 static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); m3 = Class.forName("com.zhb.jdk.proxy.IUserService").getMethod("add", new Class[] { Class.forName("java.lang.String") }); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); } catch (NoSuchMethodException nosuchmethodexception) { throw new NoSuchMethodError(nosuchmethodexception.getMessage()); } catch (ClassNotFoundException classnotfoundexception) { throw new NoClassDefFoundError(classnotfoundexception.getMessage()); } } }生成了Object类的三个方法:toString,hashCode,equals。还有我们需要被代理的方法。



