(一)什麼是代理?
在生活中經常會遇到代理,比如買房我們是去找中介,而不是自己一棟樓一棟樓去挑選,這裏的中介就是代理。代理即通過代理對象訪問目標對象,還可以在目標對象基礎上增強額外的功能。java的代理分爲靜態代理和動態代理。靜態代理即在代碼運行前,代理類就已經存在了。動態代理指代理類不是寫在代碼中的,而是在運行過程中產生的。
(二)靜態代理
靜態代理就是在代碼運行之前,代理類就已經存在了。通過一個實例來模擬靜態代理;:
以租房爲例,租房有兩種方式,一種是直接找房東去租房,另一種是找租房軟件比如自如(不是廣告),這種就是通過代理訪問目標對象。
public interface Renting {
String rent(String name);
}
接着新建一個Person類實現接口,這個類的意思是直接找房東租房,也就是目標對象:
public class Person implements Renting{
@Override
public String rent(String name) {
return "租了"+name;
}
}
然後新建代理類Ziru,可以直接通過代理類去訪問目標對象,我們前面也講到了代理類不光可以實現訪問目標對象,還可以在目標對象基礎上增強額外的功能。因此增加一條收取服務類的額外功能。
public class Ziru implements Renting{
Person person;
public Ziru(){
person=new Person();
}
@Override
public String rent(String name) {
System.out.println("收取手續費");
return person.rent(name);
}
}
public class run {
public static void main(String[] args) {
//不使用代理
// Person person=new Person();
// person.rent("碧桂園");
//使用代理
Ziru ziru=new Ziru();
System.out.println(ziru.rent("碧桂園"));
}
}
(三)動態代理
動態代理指代理類不是寫在代碼中的,而是在運行過程中產生的。java提供了兩種實現動態代理的方式,分別是基於Jdk的動態代理和基於Cglib的動態代理。
(3.1) 基於Jdk的Proxy
其中Renting和Person代碼和上面的靜態代理相同,新建一個JdkProxy類,這個類是動態代理的核心部分,實現InvocationHandler 接口。InvocationHandler 是一個接口,每個代理的實例都有一個與之關聯的 InvocationHandler 實現類,如果代理的方法被調用,那麼代理便會通知和轉發給內部的 InvocationHandler 實現類invoke,由它實現處理內容。這段代碼的另外一個核心是Proxy.newProxyInstance,該方法需要三個參數,目的是運行期間生成代理類,每個參數的功能已經寫在了註釋中。這段代碼的意思就是Proxy 動態產生的代理對象會調用 InvocationHandler 實現類invoke。
public class JdkProxy implements InvocationHandler {
Renting obj;
public Renting getProxy(Renting obj){
this.obj=obj;
//運行期間創建對象
Object proxy = Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
//obj.getClass().getClassLoader()類加載器
//obj.getClass().getInterfaces() 目標類實現的接口
//InvocationHandler對象
return (Renting) proxy;
}
//proxy 代理對象
//method 要實現的方法
//args 方法的參數
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//拿到目標方法執行結果
Object invoke = method.invoke(obj, args);
return invoke;
}
}
public class run {
public static void main(String[] args) {
Person person=new Person();
JdkProxy jdkProxy=new JdkProxy();
Renting proxy = jdkProxy.getProxy(person);
String res = proxy.rent("碧桂園");
System.out.println(res);
}
}
當調用proxy.rent()方法時,會自動執行invoke方法,以實現動態代理。
你可以在invoke方法中增加一些自己的代碼,比如我在invoke方法中寫一條
運行時就會在控制檯上打印出來,這也是Spring AOP的核心
JDK動態代理類實現了InvocationHandler接口,重寫的invoke方法。
JDK動態代理的基礎是反射機制(method.invoke(對象,參數))Proxy.newProxyInstance()
(3.2)基於Cglib的動態代理
通過CGlib也能實現動態代理,在寫代碼之前導入兩個jar包asm-3.3.1,cglib-2.2.2
CGlib動態代理的實現,其中Renting和Person代碼和靜態代理相同,這裏就不再展示。CGlib還可以直接代理類,爲了比較,這裏依舊使用接口。新建CglibProxy,實現了MethodInterceptor接口,該接口需要實現intercept方法。intercept方法中依舊調用method.invoke()方法。與Jdk不同的是創建代理對象的方式,CGlib中使用Enhance創建代理對象。
public class CglibProxy implements MethodInterceptor {
Renting obj;
//通過Enhance動態創建對象
public Renting getProxy(Renting obj){
this.obj=obj;
Enhancer enhancer=new Enhancer();
//設置父類
enhancer.setSuperclass(obj.getClass());
//設置回調方法
enhancer.setCallback(this);
return (Renting) enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object invoke = method.invoke(obj, objects);
return invoke;
}
}
public class run {
public static void main(String[] args) {
Person person=new Person();
CglibProxy cglibProxy=new CglibProxy();
Renting proxy = cglibProxy.getProxy(person);
String res = proxy.rent("碧桂園");
System.out.println(res);
}
}
(四)總結
1.JDK動態代理只能夠對接口進行代理,不能對普通的類進行代理(因爲所有生成的代理類的父類爲Proxy,Java類繼承機制不允許多重繼承);CGLIB能夠代理普通類;
2.JDK動態代理使用Java原生的反射API進行操作,在生成類上比較高效;CGLIB使用ASM框架直接對字節碼進行操作,在類的執行過程中比較高效