Java動態代理概述和手寫實現動態代理
一:前提
Spring中最重要的兩種思想:控制反轉IOC(Inversion of Control)和麵向切面程式設計AOP(Aspect-Oriented Programming),而AOP最重要的原理就是動態代理,今天我們談一下動態代理。動態代理顧名思義是代替別人做某些事,它自己不幹,讓代理幫他做。別的不多說,直接上程式碼(舉的例子可能不恰當)!
二:基於JDK的動態代理
1:因為Java的動態代理是基於介面的,所以先定義一個頂級介面Person
package dynamicProxy.jdkDynamic; /** * 頂級介面 */ public interface Person { public void speak(); }
2:寫一個Person的實現類GuangDongPerson
package dynamicProxy.jdkDynamic;
/**
* 實現類
* */
public class GuangDongPerson implements Person {
@Override
public void speak() {
System.out.println("我說的是~¥#~&~#");
}
}
3:找一個翻譯作為代理類,進行翻譯普通話。
package dynamicProxy.jdkDynamic; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class Translation implements InvocationHandler { private Person target; public Translation(Person target) { this.target = target; } public static Object getInstance(Person target){ Class<? extends Person> clazz = target.getClass(); System.out.println(clazz.getName()); Object poxy = Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), new Translation(target)); System.out.println(poxy.getClass().getName()); return poxy; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("我是翻譯,你說話我翻譯,請說話!"); Object invoke = method.invoke(target,args); System.out.println("他說的是xxxxxxxxxx"); return invoke; } }
說明:
1:Proxy.newInstance()方法
* 第一個引數為目標類的類載入器
* 第二個引數為目標類的介面集合,JDK動態代理目標類必須實現一個介面,這是為了保證生成的代理類和目標類之前的強一致性關係
* 第三個引數為InvocationHandler介面的實現類,這裡直接通過匿名內部類實現,重寫了invoke方法
2:InvocationHandler介面實現類
* 實現該類必須重寫invoke()方法,動態代理實現在代理類中該介面的實現類呼叫該方法,並在該方法中反射執行該方法完成整個動態代理流程;
* 第一個引數為生成的動態代理物件;
* 第二個引數為動態代理在客戶端執行的方法;
* 第三個引數為該方法的引數列表;
* 通過反射來完成方法呼叫;
4:測試類
package dynamicProxy.jdkDynamic;
import dynamicProxy.jdkDynamic.custom.TLGuangDongPerson;
import dynamicProxy.jdkDynamic.custom.TLPerson;
import dynamicProxy.jdkDynamic.custom.TLTranslation;
import sun.misc.ProxyGenerator;
import java.io.FileOutputStream;
public class Test {
public static void main(String[] args) throws Exception{
Person translationInstance = (Person)Translation.getInstance(new GuangDongPerson());
translationInstance.speak();
byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Person.class});
FileOutputStream outputStream = new FileOutputStream("E:\\$Proxy0.class");
outputStream.write(bytes);
outputStream.close();
// TLPerson translationInstance = (TLPerson)TLTranslation.getInstance(new TLGuangDongPerson());
// translationInstance.speak();
//
// byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Person.class});
// FileOutputStream outputStream = new FileOutputStream("E:\\$Proxy0.class");
// outputStream.write(bytes);
// outputStream.close();
}
}
5:測試結果
dynamicProxy.jdkDynamic.Test
//未被代理之前的類
dynamicProxy.jdkDynamic.GuangDongPerson
//代理之後的類
com.sun.proxy.$Proxy0
我是翻譯,你說話我翻譯,請說話!
我說的是~¥#~&~#
他說的是xxxxxxxxxx
Process finished with exit code 0
注:看到上面的輸出,可以很明顯的看代理之後的類程式設計了$Proxy0,下面是此類的反編譯結果
import dynamicProxy.jdkDynamic.Person;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements Person {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public $Proxy0(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 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 void speak() throws {
try {
super.h.invoke(this, m3, (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"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("dynamicProxy.jdkDynamic.Person").getMethod("speak");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
總結:Java動態代理使用反射機制進行代理,可以看到該虛擬類繼承Proxy類並實現了目標類頂層介面,並反射獲取了目標類中的所有方法的Method物件,同時,虛擬類也同時重寫了目標類的所有方法,並通過super.h.invoke(this, m3, (Object[])null)的方式進行呼叫。
三:手寫實現JDK動態代理
步驟:
1:TLPerson類,定義newInstance()方法,進行代理物件建立
- 在該方法中首先需要輸出代理類的.java檔案
- 編譯該.java檔案生成.class檔案
- 通過類載入器載入該.class檔案
- 生成該.class檔案的事例物件作為代理物件返回
2:TLInvocationHandler介面,定義invoke()方法,在代理類中進行目標方法呼叫
- 在.java檔案中,定義構造器,傳遞該類引用
- 通過SelfInvocationHandler實現類物件呼叫invoke()方法,實現代理方式的方法呼叫
3:TLClassLoader,自定義類載入器,繼承JDK的ClassLoader類,實現自定義的載入方式
具體操作:
1:建立頂級介面
package dynamicProxy.jdkDynamic.custom;
public interface TLPerson {
public void speak();
}
2:建立實現類
package dynamicProxy.jdkDynamic.custom;
public class TLGuangDongPerson implements TLPerson{
@Override
public void speak() {
System.out.println("我說的是~¥#~&~#");
}
}
3:使用自己的TLClassLoader代替JDK的ClassLoader,重寫findClass(String name)方法。
package dynamicProxy.jdkDynamic.custom;
import dynamicProxy.custom.SelfClassLoader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
public class TLClassLoader extends ClassLoader {
private File classPathFile;
public TLClassLoader() {
String classPath = TLClassLoader.class.getResource("").getPath();
this.classPathFile = new File(classPath);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
String className = TLClassLoader.class.getPackage().getName() + "." + name;
if (null != classPathFile) {
File classFile = new File(classPathFile, name + ".class");
if (classFile.exists()) {
FileInputStream in = null;
ByteArrayOutputStream out = null;
try {
in = new FileInputStream(classFile);
out = new ByteArrayOutputStream();
byte[] bytes = new byte[1024];
int len;
while((len = in.read(bytes)) != -1) {
out.write(bytes, 0, len);
}
return defineClass(className, out.toByteArray(), 0, out.size());
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (null != in) {
in.close();
}
if (null != out) {
out.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
return null;
}
}
4:使用自己的TLInvocationHandler代替JDK的InvocationHandler,重寫invoke(Object proxy, Method method, Object[] args)方法。
package dynamicProxy.jdkDynamic.custom;
import java.lang.reflect.Method;
public interface TLInvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
5:使用自己的TLProxy代替JDK的Proxy
package dynamicProxy.jdkDynamic.custom;
import com.sun.org.apache.regexp.internal.RE;
import dynamicProxy.custom.SelfInvocationHandler;
import dynamicProxy.custom.SelfProxy;
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class TLProxy {
private static final String LN = "\r\n";
public static Object newProxyInstance(TLClassLoader loader, Class<?>[] interfaces, TLInvocationHandler h) throws Exception {
// 動態生成原始碼
String srcClass = generateSrc(interfaces);
// 輸出Java檔案
String filePath = TLProxy.class.getResource("").getPath() + "$ProxyO.java";
System.out.println(filePath);
FileWriter fileWriter = new FileWriter(filePath);
fileWriter.write(srcClass);
fileWriter.flush();
fileWriter.close();
// 編譯Java檔案為class檔案
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
Iterable iterable = fileManager.getJavaFileObjects(filePath);
JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, null, null, iterable);
task.call();
fileManager.close();
// 載入編譯生成的class檔案到JVM
Class<?> proxyClass = loader.findClass("$ProxyO");
Constructor<?> constructor = proxyClass.getConstructor(TLInvocationHandler.class);
// 刪掉虛擬代理類
File file = new File(filePath);
file.delete();
// 返回位元組碼重組以後的代理物件
return constructor.newInstance(h);
}
private static String generateSrc(Class<?>[] interfaces) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("package dynamicProxy.jdkDynamic.custom;" + LN );
stringBuilder.append("import dynamicProxy.jdkDynamic.custom.TLPerson;" + LN);
stringBuilder.append("import java.lang.reflect.Method;" + LN);
stringBuilder.append("public class $ProxyO implements " + interfaces[0].getName() + "{" + LN);
stringBuilder.append("TLInvocationHandler h;" + LN);
stringBuilder.append("public $ProxyO(TLInvocationHandler h) {" + LN);
stringBuilder.append("this.h = h;" + LN);
stringBuilder.append("}" + LN);
for (Method method : interfaces[0].getMethods()) {
stringBuilder.append("public " + method.getReturnType().getName() + " " + method.getName() + "() {" + LN);
stringBuilder.append("try {" + LN);
stringBuilder.append("Method m = " + interfaces[0].getName() + ".class.getMethod(\"" + method.getName() + "\", new Class[]{});" + LN);
stringBuilder.append("this.h.invoke(this, m, null);" + LN);
stringBuilder.append("} catch(Throwable able) {" + LN);
stringBuilder.append("able.getMessage();" + LN);
stringBuilder.append("}" + LN);
stringBuilder.append("}" + LN );
}
stringBuilder.append("}" + LN);
return stringBuilder.toString();
}
}
6:建立手寫翻譯類
package dynamicProxy.jdkDynamic.custom;
import dynamicProxy.jdkDynamic.Translation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class TLTranslation implements TLInvocationHandler {
private TLPerson target;
public TLTranslation(TLPerson target) {
this.target = target;
}
public static Object getInstance(TLPerson target) {
try {
Class<? extends TLPerson> clazz = target.getClass();
System.out.println(clazz.getName());
Object poxy = TLProxy.newProxyInstance(new TLClassLoader(), clazz.getInterfaces(), new TLTranslation(target));
System.out.println(poxy.getClass().getName());
return poxy;
}catch (Exception e){
e.printStackTrace();
}
return null;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
System.out.println("我是翻譯,你說話我翻譯,請說話!");
Object invoke = method.invoke(target,args);
System.out.println("他說的是xxxxxxxxxx");
return invoke;
}
}
7:測試類
package dynamicProxy.jdkDynamic;
import dynamicProxy.jdkDynamic.custom.TLGuangDongPerson;
import dynamicProxy.jdkDynamic.custom.TLPerson;
import dynamicProxy.jdkDynamic.custom.TLTranslation;
import sun.misc.ProxyGenerator;
import java.io.FileOutputStream;
public class Test {
public static void main(String[] args) throws Exception{
// Person translationInstance = (Person)Translation.getInstance(new GuangDongPerson());
// translationInstance.speak();
//
// byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Person.class});
// FileOutputStream outputStream = new FileOutputStream("E:\\$Proxy0.class");
// outputStream.write(bytes);
// outputStream.close();
TLPerson translationInstance = (TLPerson)TLTranslation.getInstance(new TLGuangDongPerson());
translationInstance.speak();
byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Person.class});
FileOutputStream outputStream = new FileOutputStream("E:\\$Proxy0.class");
outputStream.write(bytes);
outputStream.close();
}
}
8:測試結果
dynamicProxy.jdkDynamic.custom.TLGuangDongPerson
/F:/project/TimeTest/target/classes/dynamicProxy/jdkDynamic/custom/$ProxyO.java
dynamicProxy.jdkDynamic.custom.$ProxyO
我是翻譯,你說話我翻譯,請說話!
我說的是~¥#~&~#
他說的是xxxxxxxxxx
Process finished with exit code 0
這僅是一個理解動態代理的思路,可能會給你一點啟發,共同進步!