代理模式是一種結構型設計模式,簡單解釋就是使用代理對象調用目標對象,並且在不改變源碼的情況下,實現對目標對象的功能擴展。
public class Performer{
public void perform(){
System.out.println("表演才藝");
}
}
他在表演才藝之前,先跟觀衆打招呼,表演完才藝之後,向觀衆致謝
public void perform(){
System.out.println("各位觀衆朋友們大家好");
System.out.println("表演才藝");
System.out.println("感謝大家的觀看");
}
核心任務是表演才藝,非核心任務是打招呼跟致謝,我們想把這兩項分開。表演者類設計爲目標類,只保留表演才藝的核心任務。再設計一個代理類,實現對目標類的調用與擴展,代理類中實現打招呼跟致謝的功能,這就是我們用到的代理模式。
代理模式分爲靜態代理跟動態代理,動態代理又分爲jdk動態代理跟cglib動態代理
public interface IPerform {
public void perform();
}
public class Performer implements IPerform {
@Override
public void perform() {
System.out.println("表演才藝");
}
}
public class PerformProxy implements IPerform {
//接收目標對象
private IPerform target;
public PerformProxy(IPerform perform) {
this.target = perform;
}
@Override
public void perform() {
//表演前先向觀衆們問好
System.out.println("各位觀衆朋友們大家好");
//調用目標方法
target.perform();
//表演完成向觀衆致謝
System.out.println("感謝大家的觀看");
}
}
public class Test {
public static void main(String[] args) {
//目標對象
IPerform perform = new Performer();
//代理對象,將目標對象注入
IPerform performProxy = new PerformProxy(perform);
performProxy.perform();
}
}
靜態代理中,將目標類注入到代理類中,實現在代理類中調用目標類的方法,並擴展目標類的功能。但是代理對象需要與目標對象實現一樣的接口,代理對象必須提前寫出,如果接口層發生了變化,代理對象的代碼也要進行維護。如果能在運行時動態地寫出代理對象,不但減少了一大批代理類的代碼,也少了不斷維護的煩惱。這種方式就是接下來的動態代理
jdk動態代理代理類所在的包爲:java.lang.reflect.Proxy,調用Proxy類的靜態方法newProxyInstance,該方法會返回代理類對象。
newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )接收的三個參數依次爲:
ClassLoader loader:指定當前目標對象使用類加載器,寫法固定
Class<?>[] interfaces:目標對象實現的接口的類型,寫法固定
InvocationHandler h:事件處理接口,需傳入一個實現類,一般直接使用匿名內部類
public interface IPerform {
public void perform();
}
public class Performer implements IPerform {
@Override
public void perform() {
System.out.println("表演才藝");
}
}
public class PerformProxy {
//接收目標對象
private IPerform target;
public PerformProxy(IPerform perform) {
this.target = perform;
}
//使用jdk動態代理,爲目標對象生成代理對象
public Object getProxyInstance(){
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(
Object proxy,
Method method,
Object[] args)
throws Throwable {
//表演前先向觀衆們問好
System.out.println("各位觀衆朋友們大家好");
//調用目標方法
Object returnValue = method.invoke(target, args);
//表演完成向觀衆致謝
System.out.println("感謝大家的觀看");
return returnValue;
}
});
}
}
public class Test {
public static void main(String[] args) {
//目標對象
IPerform perform = new Performer();
//代理對象,將目標對象注入
PerformProxy performProxy = new PerformProxy(perform);
IPerform proxy = (IPerform)performProxy.getProxyInstance();
//通過代理對象調用目標對象方法
proxy.perform();
}
}
jdk動態代理中的目標對象必須實現接口,否則無法使用該代理方式。如果目標對象沒有實現接口,我們可以使用cglib動態代理。
需要引入cglib的jar文件與asm的jar文件,cglib依賴於asm。由於Spring的核心包中已經包括了Cglib功能,所以也可以直接引入spring-core-3.2.5.jar
目標對象的方法如果爲final/static,那麼就不會被攔截,即不會執行目標對象額外的業務方法
public class Performer {
public void perform() {
System.out.println("表演才藝");
}
}
public class PerformProxy implements MethodInterceptor {
//接收目標對象
private Object target;
public PerformProxy(Object perform) {
this.target = perform;
}
//獲取目標對象的代理對象
public Object getProxyInstance(){
//工具類
Enhancer en = new Enhancer();
//設置父類對象
en.setSuperclass(this.target.getClass());
//設置回調函數
en.setCallback(this);
//創建子類,也就是代理對象
return en.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//表演前先向觀衆們問好
System.out.println("各位觀衆朋友們大家好");
//執行目標對象的方法
Object returnValue = method.invoke(target, objects);
//表演完成向觀衆致謝
System.out.println("感謝大家的觀看");
return returnValue;
}
}
public class Test {
public static void main(String[] args) {
//目標對象
Performer perform = new Performer();
PerformProxy performProxy = new PerformProxy(perform);
//生成代理對象
Performer proxy = (Performer)performProxy.getProxyInstance();
proxy.perform();
}
}
在Spring的AOP編程中: 如果加入容器的目標對象有實現接口,用JDK代理 如果目標對象沒有實現接口,用Cglib代理