代理就是有一些工作你不屑於自己做的,或者不方便自己做的,這個時候你就請別人幫你做,拿我最喜歡的偶像坤坤做例子,他開演唱會肯定不會自己收門票,不會自己清場打掃衛生,所以就需要代理來代替他完成一整場演唱會的工作。
靜態代理,這種方式最好理解,但是也是最差的一種代理方式,屬於硬編碼的一種,寫死在代碼中,編譯期間就產生了的代理關係。
先定義一個坤坤的接口。
public interface CXK {
public void show();
}
再定義一個接口的實現類,這個實現類通過添加方法的方式來達到接口功能增強的目的。
public class CXK_Agent implements CXK {
public void getMoney(){
System.out.println("收錢纔開始表演......");
show();
over();
}
@Override
public void show() {
System.out.println("唱,跳,rap,籃球");
}
public void over(){
System.out.println("打掃清場衛生");
}
}
調用的時候肯定是通過創建一個坤坤團隊的代理類來開演唱會,這樣團隊才能完成一整套工作。
靜態代理的演唱會圓滿結束!
忽然有一天,坤坤沒有養一羣固定團隊了,他在要開演唱會的時候才臨時找外包團隊。這就是動態代理。
動態代理不單隻負責維護坤坤的演唱會,還會維護TFBoy的演唱會,周杰倫的演唱會等等。所以就不能硬編碼。
JDK動態代理
JDK動態代理就是針對接口的增強,針對接口的代理,所以還是先創建最愛的坤坤接口。
public interface CXK {
public void show();
}
然後創建一個實現類,實現坤坤的唱跳Rap表演節目。
public class CXK_MAN implements CXK{
@Override
public void show() {
System.out.println("唱,跳,rap,籃球");
}
}
最後我們再給坤坤包裝團隊。也就是動態增強的代理方法。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class CXK_proxy implements InvocationHandler {
private Object man;
public CXK_proxy(Object man){
this.man = man;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("收錢纔開始表演......");
Object mans = method.invoke(man,args);
System.out.println("打掃清場衛生");
return mans;
}
}
可以看出是給一個有參構造方法,預留好演員的位置,方便把坤坤融入該外包團隊,然後實現InvocationHandler接口,重寫裏面的invoke方法,invoke方法就是描述團隊一整場演唱會下來都做了什麼的方法。也就是增強方法。
最後就是到演出的時候,先創建一個坤坤的實例,把坤坤放進增強類的有參構造方法裏,然後使用Proxy.newProxyInstance方法,把坤坤類的類加載器和坤坤實現的接口和坤坤的增強型團隊都傳進去。
public class test {
public static void main(String[] args) {
CXK_MAN man = new CXK_MAN();
CXK_proxy handlers = new CXK_proxy(man);
CXK cxk_man = (CXK) Proxy.newProxyInstance(man.getClass().getClassLoader(), man.getClass().getInterfaces(),handlers);
cxk_man.show();
}
}
記得返回那個必須強轉型,類型必須是坤坤的接口,反正就記住一句話,JDK代理就是圍繞着接口的代理。
執行一下:
演唱會圓滿結束。
坤坤開多少場演唱會,團隊就跟他合作多少次。
CGLIB的動態代理
這也是spring框架已經集成的動態代理,AOP的底層其實就有它,如果是你自己單獨的Java代碼的話需要加個jar包依賴到maven的pom文件。它底層其實是通過攔截器實現的增強。
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
由於GCLIB是針對實現類的方法的增強型方法代理,所以我們可以不提供接口,它這個動態代理方法不要求接口。
老規矩,坤坤先表演拿手才藝
public class CXK_MAN {
public void show() {
System.out.println("唱,跳,rap,籃球");
}
}
還是要給一個代理類的
public class CXK_proxy implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("收錢纔開始表演......");
Object cxk = methodProxy.invokeSuper(o,objects);
System.out.println("打掃清場衛生");
return cxk;
}
}
然後是測試類
public class test {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(CXK_MAN.class);
enhancer.setCallback(new CXK_proxy());
CXK_MAN man = (CXK_MAN) enhancer.create();
man.show();
}
}
執行一康康
可以看到演唱會順利結束。
所以總結:
(1)JDK是針對接口做的代理。
(2)CGLIB是針對實例對象的方法做的代理。
(3)Spring中的AOP底層是CGLIB動態代理。
(4)靜態代理在實際項目中還可能用得上,動態代理不是搞科研不是搞框架的話,業務代碼是真的很少接觸。