前面講的都是一些代理類生成的一些準備作業,本節講講代理類如何生成出來的一個程序,
java.lang.reflect.Proxy.ProxyClassFactory#apply
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
...
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
...
}
sun.misc.ProxyGenerator#generateProxyClass(java.lang.String, java.lang.Class<?>[], int)
public static byte[] generateProxyClass(final String name,
Class<?>[] interfaces,
int accessFlags)
{
ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags);
// !!!! 生成代理類的位元組碼檔案
final byte[] classFile = gen.generateClassFile();
// 保存代理類的class檔案 ??
// 這樣設定就可以保存了,System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
if (saveGeneratedFiles) {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
try {
int i = name.lastIndexOf('.');
Path path;
if (i > 0) {
Path dir = Paths.get(name.substring(0, i).replace('.', File.separatorChar));
Files.createDirectories(dir);
path = dir.resolve(name.substring(i+1, name.length()) + ".class");
} else {
path = Paths.get(name + ".class");
}
Files.write(path, classFile);
return null;
} catch (IOException e) {
throw new InternalError(
"I/O exception saving generated file: " + e);
}
}
});
}
return classFile;
}
代理類的具體實作就在sun.misc.ProxyGenerator#generateClassFile, 我們來看看它的原始碼
一、generateClassFile
sun.misc.ProxyGenerator#generateClassFile,根據原始碼中的注釋,我們把原始碼分成三部
步驟一: 為所有方法組裝ProxyMethod物件;檢查相同簽名(方法名 + 引數串列)的代理方法,其回傳型別是否兼容
private byte[] generateClassFile() {
// Obeject的hashcode、equals,toString添加到proxyMethods中,以便代理類中存在這些方法,
// 它比代理介面中的方法先完成,這樣可以保證代理類的方法能重寫這三個方法
addProxyMethod(hashCodeMethod, Object.class);
addProxyMethod(equalsMethod, Object.class);
addProxyMethod(toStringMethod, Object.class);
// 記錄來自代理介面的方法
for (Class<?> intf : interfaces) {
for (Method m : intf.getMethods()) {
addProxyMethod(m, intf);
}
}
// 檢查相同方法簽名的代理方法,回傳型別是否兼容
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
checkReturnTypes(sigmethods);
}
....
}
步驟二: 為所有fields和methods組裝對應的結構
private byte[] generateClassFile() {
...
try {
// 為代理類生成構造器
methods.add(generateConstructor());
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
for (ProxyMethod pm : sigmethods) {
// 添加方法的Method物件的靜態欄位, eg, private static Method m1;
fields.add(new FieldInfo(pm.methodFieldName,
"Ljava/lang/reflect/Method;",
ACC_PRIVATE | ACC_STATIC));
// 為代理類生成代碼并添加它
methods.add(pm.generateMethod());
}
}
// 為代理類生成初始化方法
methods.add(generateStaticInitializer());
} catch (IOException e) {
throw new InternalError("unexpected I/O Exception", e);
}
// JVM的規定!
if (methods.size() > 65535) {
throw new IllegalArgumentException("method limit exceeded");
}
if (fields.size() > 65535) {
throw new IllegalArgumentException("field limit exceeded");
}
...
}
步驟三: Write the final class file.!
private byte[] generateClassFile() {
...
// 保證下面的這些東西要存在常量池的索引中,在開始寫類檔案之前
cp.getClass(dotToSlash(className));
cp.getClass(superclassName);
for (Class<?> intf: interfaces) {
cp.getClass(dotToSlash(intf.getName()));
}
/*
* Disallow new constant pool additions beyond this point, since
* we are about to write the final constant pool table.
*/
cp.setReadOnly();
ByteArrayOutputStream bout = new ByteArrayOutputStream();
DataOutputStream dout = new DataOutputStream(bout);
try {
/*
* Write all the items of the "ClassFile" structure.
* See JVMS section 4.1.
*/
// u4 magic;
dout.writeInt(0xCAFEBABE);
// u2 minor_version;
dout.writeShort(CLASSFILE_MINOR_VERSION);
// u2 major_version;
dout.writeShort(CLASSFILE_MAJOR_VERSION);
cp.write(dout); // (write constant pool)
// u2 access_flags;
dout.writeShort(accessFlags);
// u2 this_class;
dout.writeShort(cp.getClass(dotToSlash(className)));
// u2 super_class;
dout.writeShort(cp.getClass(superclassName));
// u2 interfaces_count;
dout.writeShort(interfaces.length);
// u2 interfaces[interfaces_count];
for (Class<?> intf : interfaces) {
dout.writeShort(cp.getClass(
dotToSlash(intf.getName())));
}
// u2 fields_count;
dout.writeShort(fields.size());
// field_info fields[fields_count];
for (FieldInfo f : fields) {
f.write(dout);
}
// u2 methods_count;
dout.writeShort(methods.size());
// method_info methods[methods_count];
for (MethodInfo m : methods) {
m.write(dout);
}
// u2 attributes_count;
dout.writeShort(0); // (no ClassFile attributes for proxy classes)
} catch (IOException e) {
throw new InternalError("unexpected I/O Exception", e);
}
return bout.toByteArray();
...
}
在try中的代碼,是在寫ClassFile(位元組碼)的結構, 可以參考Chapter 4. The class File Format
上面我們看了代理類的位元組碼的生成,我們現在再看一看生成的代理類(修改了少量地方),是不是有不一樣的感覺了,
// 生成的代理類繼承了Proxy
public final class Proxy0 extends Proxy implements UpanSell {
// Method的靜態欄位
private static Method m1;
private static Method m2;
private static Method m0;
private static Method m3;
// 構造器
public Proxy0(InvocationHandler var1) {
super(var1);
}
// 為代理類生成的方法 ??-------
@Override
public final boolean equals(Object var1) {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
@Override
public final String toString() {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
@Override
public final int hashCode() {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
@Override
public final float sell(int var1) {
try {
return (Float)super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
m3 = Class.forName("com.ukyu.dynamicproxy.service.UpanSell").getMethod("sell", Integer.TYPE);
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
我們將
UpanSell proxy = (UpanSell) Proxy.newProxyInstance(factory.getClass().getClassLoader(),
factory.getClass().getInterfaces(),
handler
);
改為 ->
UpanSell proxy = new Proxy0(handler);
輸出的效果是一樣的,
二、總結
我們將具體生成代理類的程序走了一遍,結合原始碼食用更佳
tips: 只能給實作了介面的類,使用JDK動態代理來創建代理類
若想給沒有實作介面的類,動態生成代理類,就需要使用CGLIB,可以參考一下CGLib動態代理這篇博客
三、參考
- Chapter 4. The class File Format
- CGLib動態代理
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/346850.html
標籤:Java
