簡介
代理模式在Java中有很多應用場景,而代理又分靜態代碼和動態代理,靜態代理是撰寫、編譯或加載時織入代碼實作,而動態代理則在運行時實作,簡單而言,靜態代理是在運行前就已經存在,而動態代理則在運行時才存在的,而常用的動態代理有兩種實作:
- JDK Proxy: JDK Proxy是JDK自帶的,不需要引入外部庫,通過實作介面進行代理;
- CGLib: CGLib是引入第三方庫,通過ASM技術來實作位元組碼的生成;通過繼承的方式來實作,
現在我們來通過代碼分別展示一下兩種方式,
JDK Proxy
JDK Proxy是通過實作介面來實作代理的,我們先定義一個介面:
public interface Flyable {
String fly(String route);
}
接著有一個實作類:
public class Bird implements Flyable {
@Override
public String fly(String route) {
System.out.println("Route: " + route);
return route;
}
}
然后我們需要定義一個InvocationHandler來改動方法的邏輯,就是目標被代理后有什么不同:
public class FlyableInvocation implements InvocationHandler {
private final Flyable target;
public FlyableInvocation(Flyable target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long start = System.nanoTime();
System.out.println(target + ": ===JDK proxy===");
Object result = method.invoke(this.target, args);
System.out.println(target + ": ===JDK proxy===");
long end = System.nanoTime();
System.out.println("Executing time: " + (end - start) + " ns");
return result;
}
}
這里我們在方法呼叫前后加了日志,同時也計算了一下方法的執行時間,
最終在呼叫的時候如下:
public class JDKDynamicProxy {
public static void main(String[] args) {
ClassLoader classLoader = JDKDynamicProxy.class.getClassLoader();
Class<?>[] interfaces = Bird.class.getInterfaces();
Bird bird = new Bird();
Flyable flyable = (Flyable) Proxy.newProxyInstance(classLoader, interfaces, new FlyableInvocation(bird));
flyable.fly("Go to pkslow.com");
}
}
通過Proxy.newProxyInstance方法會生成一個代理的實體,執行這個實體的方法,而原有實體bird被代理了,
執行結果如下:
com.pkslow.basic.jdk.Bird@3551a94: ===JDK proxy===
Route: Go to pkslow.com
com.pkslow.basic.jdk.Bird@3551a94: ===JDK proxy===
Executing time: 18195736 ns
查看代理類
我們還可以查看生成的代理類,可以通過添加VM引數:
# JDK 8
-Dsum.misc.ProxyGenerator.saveGeneratedFiles=true
# JDK 11
-Djdk.proxy.ProxyGenerator.saveGeneratedFiles=true
當然,也可以在Java代碼中設定系統屬性來實作,
設定完成,再執行程式,就會生成代理類的.class檔案,

CGLib
CGLib是通過繼承來實作的,我們先來定義一個類:
public class Animal {
public String talk(String str) {
System.out.println("Talking: " + str);
return str;
}
}
然后定義一個Interceptor,這個類的作用就是生成代理實體,且定義如何改變目標方法的執行:
public class CGLibProxy<T> implements MethodInterceptor {
private T target;
public T getInstance(T target) {
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
enhancer.setCallback(this);
return (T) enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
long start = System.nanoTime();
System.out.println(target + ": ===CGLib proxy===");
Object result = methodProxy.invoke(this.target, args);
System.out.println(target + ": ===CGLib proxy===");
long end = System.nanoTime();
System.out.println("Executing time: " + (end - start) + " ns");
return result;
}
}
這里同樣是在方法前后加了日志,同時記錄時長,
呼叫如下:
public class CGLibDynamicProxy {
public static void main(String[] args) {
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/Users/larry/IdeaProjects/pkslow-samples/java-basic/jdk-cglib-proxy/target/cglib_proxy_classes");
CGLibProxy<Animal> cgLibProxy = new CGLibProxy<>();
Animal animal = cgLibProxy.getInstance(new Animal());
animal.talk("Hi, pkslow");
}
}
這里設定系統屬性是為了把生成的代理類輸出到.class檔案中,方便學習查看,
執行結果如下:
com.pkslow.basic.cglib.Animal@57855c9a: ===CGLib proxy===
Talking: Hi, pkslow
com.pkslow.basic.cglib.Animal@57855c9a: ===CGLib proxy===
Executing time: 28396871 ns
總結
JDK Proxy本質上使用的是反射的機制,而CGLib使用的是ASM,CGLib速度會更好,但它們都不支持final的類和方法,因為通過介面和繼承都無法改變final方法,
代碼請看GitHub: https://github.com/LarryDpk/pkslow-samples
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/543173.html
標籤:其他
