摘要
什麼是Java代理呢?Java的代理就是客戶類不再直接和委托類打交道,而是通過一個中間層來通路,而這個中間層就是代理。比如當我們想給某個類中的方法加一些額外的操作處理,就可以給這個類建立一個代理類,這個類不僅包含了原來類的方法功能,而且還在原來的基礎上增加了額外的處理功能。
為什麼要這樣做呢?是因為增加代理還是有好處的:
- 可以隐藏委托類的實作。
- 可以實作用戶端和委托類之間的解耦,在不修改委托類代碼的情況下能夠添加一些額外操作(
、解耦
、靈活
)擴充性強
1、靜态代理
靜态代理就是代理類在編譯期間就建立好了,是手動建立的類。即代理類和委托類的關系在程式運作前就已經存在了。她适合用于代理類比較少且确定的情況,如果委托類有方法很多就會很難受,要在代理類中寫一堆的代理方法。
實作步驟
- 定義接口和接口的實作類。
- 定義接口的代理對象,并将接口的執行個體注入到代理對象中。
- 然後通過代理對象調用真實的實作類。
//接口
public interface IHelloService {
String sayHello(String name);
}
//實作類
public class Helloservice implements IHelloService {
public String sayHello(String word) {
System.out.println("I am static proxy,"+word);
return "I am static proxy,"+word;
}
}
//靜态代理
public class StaticProxyHello implements IHelloService {
// 定義接口的代理對象,并将接口的執行個體注入到代理對象中
private IHelloService helloService = new Helloservice();
public String sayHello(String message) {
//調用
beforeProcess();
String result = helloService.sayHello(message);
//調用
afterProcess();
return result;
}
private void beforeProcess() {
System.out.println("我是委托類方法處理之前要執行的方法。。。。");
}
private void afterProcess() {
System.out.println("我是委托類處理之後執行的方法。。。。");
}
}
//測試
public class proxyTest {
public static void main(String[] args) {
StaticProxyHello proxy = new StaticProxyHello();
proxy.sayHello("我是靜态代理。。。");
}
}
Java動态代理
代理類是在程式運作時建立的代理方式稱為
動态代理
,相比于靜态代理而言,動态代理的優勢可以對代理類中的方法進行統一的處理,而不用修改每個代理類中的方法。在java動态代理中主要涉及兩個類,
java.lang.reflect.InvocationHandler
和
java.lang.reflect.Proxy
,需要建立一個代理類來實作
InvocationHandler
接口,這個接口中隻有一個方法
invoke
,我們在對委托類中所有的方法調用都會變成是調用
invoke
方法。這樣我們就可以在
invoke
方法中添加一些特定的邏輯進行處理。
實作步驟
//委托類接口
public interface IHelloService {
String sayHello(String message);
}
//委托類具體實作
public class HelloService implements IHelloService {
@Override
public String sayHello(String message) {
System.out.println("執行了sayHello方法....");
return "消息内容:" + message;
}
}
//代理類
public class HelloProxyInvocationHandler implements InvocationHandler {
private Object obj;
public HelloProxyInvocationHandler(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
beforeProcess();
Object result = method.invoke(obj, args);
afterProcess();
return result;
}
private void beforeProcess() {
System.out.println("增加方法調用之前執行的額外操作...");
}
private void afterProcess() {
System.out.println("增加方法調用之後執行的額外操作...");
}
}
//測試動态代理類
public class MainProxy {
public static void main(String[] args) {
HelloProxyInvocationHandler proxyInvocationHandler = new HelloProxyInvocationHandler(new HelloService());
IHelloService helloProxy = (IHelloService) Proxy.newProxyInstance(HelloService.class.getClassLoader(),
new Class<?>[]{IHelloService.class}, proxyInvocationHandler);
helloProxy.sayHello("我是jdk動态代理");
}
}
上面的測試動态代理類中, 我們調用
Proxy
類的
newProxyInstance
方法來擷取一個代理類執行個體。這個代理類實作了我們指定的接口并且會把方法調用分發到指定的調用處理器。首先通過
newProxyInstance
方法擷取代理類的執行個體, 之後就可以通過這個執行個體調用代理類的方法,對代理類的方法調用都會調用invoke方法,在invoke方法中我們調用委托類的對應方法,然後加上自己的處理邏輯。
java動态代理最大的特點就是動态生成的代理類和委托類實作同一個接口。java 動态代理其實内部是通過反射機制實作的,也就是已知的一個對象,在運作的時候動态調用它的方法,并且調用的時候還可以加一些自己的邏輯在裡面。
CGlib動态代理
JDK的動态代理依賴于接口,當我們隻有類而沒有接口的時候,我們需要借助一個三方架構
CGlib
來實作動态代理。
CGLIB
代理是針對類來實作代理的,原理是對指定的委托類生成一個子類并重寫其中業務方法來實作代理。但因為采用的是繼承,是以不能對final修飾的類進行代理。final修飾的類不可繼承。
實作步驟
//委托類
public class HelloService {
public String sayHello(String message) {
System.out.println("目标對象sayHello執行了....");
return "消息内容:" + message;
}
}
//對方法調用攔截以及回調
public class HelloInterceptor implements MethodInterceptor {
/**
* 用于生成 Cglib 動态代理類工具方法
* @param target 委托類
* @return
*/
public Object initCglib(Class target) {
Enhancer enhancer = new Enhancer();// 為代理類指定需要代理的類,也即是父類
enhancer.setSuperclass(target);// 設定方法攔截器回調引用,對于代理類上所有方法的調用,都會調用CallBack,而Callback則需要實作intercept() 方法進行攔截
enhancer.setCallback(this);// 擷取動态代理類對象并傳回
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
beforeProcess();
Object result = methodProxy.invokeSuper(o, args);
afterProcess();
return result;
}
private void beforeProcess() {
System.out.println("增加方法調用之前執行的額外操作...");
}
private void afterProcess() {
System.out.println("增加方法調用之後執行的額外操作...");
}
}
//測試
public class MainCglib {
public static void main(String[] args) {
HelloInterceptor interceptor = new HelloInterceptor();
HelloService helloService = (HelloService) interceptor.initCglib(HelloService.class);
helloService.sayHello("我是cglib實作的動态代理");
}
}
對于需要被代理的類,它隻是動态生成一個子類以覆寫非final的方法,同時綁定鈎子回調自定義的攔截器。
總結
靜态代理相對而言比較容易了解, 需要委托類和代理類實作同一個接口, 然後在代理類中調用真正實作類, 并且靜态代理的關系在編譯期間就已經确定了。而動态代理的關系是在運作期間确定的。靜态代理實作簡單,适合于代理類較少且确定的情況,而動态代理則給我們提供了更大的靈活性。
JDK動态代理所用到的代理類在程式調用到代理類對象時才由JVM真正建立,JVM根據傳進來的業務實作類對象以及方法名,動态地建立了一個代理類的class檔案并被位元組碼引擎執行,然後通過該代理類對象進行方法調用。
靜态代理和動态代理都是基于接口實作的, 而對于那些沒有提供接口隻是提供了實作類的而言, 就隻能選擇CGLIB動态代理了。
Cglib和jdk動态代理的差別?
- JDK動态代理基于Java反射機制實作, 必須要實作了接口的業務類才能用這種方法生成代理對象。
- CGLIB動态代理基于ASM架構通過生成業務類的子類來實作。
靜态代理、JDK動态代理、CGlib動态代理優缺點?
靜态代理:代理對象和實際對象都實作同一個接口,在代理對象中指向的是實際對象的執行個體,這樣對外暴露的是代理對象而真正調用的是真實對象。
- 優點:可以很好的保護實際對象的業務邏輯對外暴露,進而提高安全性。
- 缺點:不同的接口要有不同的代理類實作,會很備援
JDK動态代理:為了解決靜态代理中,生成大量的代理類造成的備援;JDK動态代理隻需要實作InvocationHandler接口,重寫invoke方法便可以完成代理的實作,jdk的代理是利用反射生成代理類Proxyxx.class代理類位元組碼,并生成對象。jdk動态代理之是以隻能代理接口是因為代理類本身已經繼承了Proxy,而java是不允許多重繼承。
- 優點:解決了靜态代理中備援的代理實作類問題。
- 缺點:JDK 動态代理是基于接口設計實作的,如果沒有接口,會抛異常。
CGLIB動态代理: 由于JDK動态代理限制了隻能基于接口設計,而對于沒有接口的情況,JDK方式解決不了;CGLib采用了非常底層的位元組碼技術,其原理是通過位元組碼技術為一個類建立子類,并在子類中采用方法攔截的技術攔截所有父類方法的調用,順勢織入橫切邏輯,來完成動态代理的實作。實作方式實作MethodInterceptor接口,重寫intercept方法,通過Enhancer類的回調方法來實作。
但是CGLib在建立代理對象時所花費的時間卻比JDK多得多,是以對于單例的對象,因為無需頻繁建立對象,用CGLib合适,反之,使用JDK方式要更為合适一些。同時,由于 CGLib 由于是采用動态建立子類的方法,對于final方法,無法進行代理。
- 優點:沒有接口也能實作動态代理,而且采用位元組碼增強技術,性能也不錯。
- 缺點:技術實作相對難了解些。
javassist動态封裝 ASM架構?
參看:
https://juejin.cn/post/6924211695921397768