java动态代理主要是有Proxy和InvocationHandler两个类实现的
一、我先来看下如何使用Proxy和InvocationHandler来实现动态代理
1.先定义一个接口MarkMan
-
再定义的一个类MarkManFactory实现MarkMan
-
然后定义一个MyInvocationHandler实现InvocationHandler。
在里面定义一个setFactory(Object obj)方法把要代理的对象传入进去,然后定义个newProxyInstance方法,方法里面调用Proxy.newProxyInstance()方法获取代理对象。
实现InvocationHandler的invoke方法
4.然后在main方法中进行调用
我们运行下程序,结果打压如下
二、通过调试模式我们发现,动态代理里,代理类的类名是这样的:
[图片上传失败...(image-9e5ea5-1613977035476)]
这个代理类为何是这个名字?它是如何执行被代理对象的相关方法呢?我们在java文件编译后的目录里其实找不到这个名为$Proxy0的class文件的。带着这个问题我来看看Proxy和InvocationHandler的源码 看它们是如何实现动态代理的
查看Proxy的newProxyInstance方法源码
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,Class>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException {
Objects.requireNonNull(h);
........
/*
* Look up or generate the designated proxy class.
*/
//获取clazz对象
Class> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
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;
}
});
}
//通过构造函数new一个实例对象
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);
}
}
通过源码我们可以看到是newProxyInstance是通过获取class对象,然后通过class对象获取构造函数,通过构造函数new一个实例对象返回 接下来我们看下getProxyClass0(loader, intfs)方法
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);
}
通过源码 我们发现getProxyClass0就是从缓存中去获取,继续看WeakCache的get方法
这里我们主要看下subKeyFactory.apply(key, parameter)方法 ,其余都是验证跟判断
public V get(K key, P parameter) {
....省略
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
Supplier supplier = valuesMap.get(subKey);
Factory factory = null;
while (true) {
if (supplier != null) {
V value = supplier.get();
if (value != null) {
return value;
}
}
if (factory == null) {
factory = new Factory(key, parameter, subKey, valuesMap);
}
if (supplier == null) {
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
supplier = factory;
}
} else {
if (valuesMap.replace(subKey, supplier, factory)) {
supplier = factory;
} else {
// retry with current supplier
supplier = valuesMap.get(subKey);
}
}
}
}
subKeyFactory是BiFunction接口,通过查看实现类发现是ProxyClassFactory类,接下来看ProxyClassFactory 的apply方法
public Class> apply(ClassLoader loader, Class>[] interfaces) {
....省略
String proxyPkg = null; // package to define proxy class in
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
long num = nextUniqueNumber.getAndIncrement();
** //代理类的名称 这里就是我们的要找的答案 $Proxy0 **
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
throw new IllegalArgumentException(e.toString());
}
}
通过源码我们发现通过ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags) 转换成byte[]数组,然后通过defineClass0方法转换成Class字节码返回,
public static byte[] generateProxyClass(final String var0, Class>[] var1, int var2) {
ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
final byte[] var4 = var3.generateClassFile();
if (saveGeneratedFiles) {
AccessController.doPrivileged(new PrivilegedAction() {
public Void run() {
try {
int var1 = var0.lastIndexOf(46);
Path var2;
if (var1 > 0) {
Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
Files.createDirectories(var3);
var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
} else {
var2 = Paths.get(var0 + ".class");
}
Files.write(var2, var4, new OpenOption[0]);
return null;
} catch (IOException var4x) {
throw new InternalError("I/O exception saving generated file: " + var4x);
}
}
});
}
return var4;
}
//本地方法
private static native Class> defineClass0(ClassLoader loader, String name,
byte[] b, int off, int len);
为了查看byte[]数组里面的内容我们可以自己定义个ProxyUtils工具类把byte[]写到文件中,查看里面的内容
public class ProxyUtils {
public static void generateClassFile(Class clazz,String proxyName){
/*ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, new Class[]{clazz});
String paths = clazz.getResource(".").getPath();
System.out.println(paths);
FileOutputStream out = null;
try {
out = new FileOutputStream(paths+proxyName+".class");
out.write(proxyClassFile);
out.flush();
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
运行代码 我看到生成的文件是以Proxy1这么的名称
查看里面的内容
public final class $Proxy0 extends Proxy implements Person
{
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
/**
*注意这里是生成代理类的构造方法,方法参数为InvocationHandler类型,看到这,是不是就有点明白
*为何代理对象调用方法都是执行InvocationHandler中的invoke方法,而InvocationHandler又持有一个
*被代理对象的实例,不禁会想难道是....? 没错,就是你想的那样。
*
*super(paramInvocationHandler),是调用父类Proxy的构造方法。
*父类持有:protected InvocationHandler h;
*Proxy构造方法:
* protected Proxy(InvocationHandler h) {
* Objects.requireNonNull(h);
* this.h = h;
* }
*
*/
public $Proxy0(InvocationHandler paramInvocationHandler) throws {
super(paramInvocationHandler);
}
//这个静态块本来是在最后的,我把它拿到前面来,方便描述
static{
try {
//看看这儿静态块儿里面有什么,是不是找到了saleMan方法。请记住saleMan通过反射得到的名字m3,其他的先不管
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("proxy.MarkManFactory").getMethod("saleMan", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
} catch (NoSuchMethodException localNoSuchMethodException){
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}catch (ClassNotFoundException localClassNotFoundException){
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
/**
*
*这里调用代理对象的saleMan方法,直接就调用了InvocationHandler中的invoke方法,并把m3传了进去。
*this.h.invoke(this, m3, null);这里简单,明了。
*来,再想想,代理对象持有一个InvocationHandler对象,InvocationHandler对象持有一个被代理的对象,
*再联系到InvacationHandler中的invoke方法。嗯,就是这样。
*/
public final void saleMan() throws {
try {
this.h.invoke(this, m3, null);
return;
}catch (Error|RuntimeException localError){
throw localError;
}catch (Throwable localThrowable){
throw new UndeclaredThrowableException(localThrowable);
}
}
//注意,这里为了节省篇幅,省去了toString,hashCode、equals方法的内容。原理和saleMan方法一毛一样。
}
这的h是什么呢 我们看下Proxy类可以看到
这个h的实例来自哪里?不就是我们在创建代理类的实例时传入的吗?
到此整个动态代理的源码分析就结束了