靜態代理:在使用靜態代理時,被代理對象(目標對象)與代理對象需要一起實現相同的接口或者是繼承相同父類,因此要定義一個接口或抽象類.
例子:明星與經紀人之間就是被代理和代理的關係,明星出演活動的時候,明星就是一個目標對象,他只要負責活動中的節目,而其他瑣碎的事情就交給他的代理人(經紀人)
代碼:
抽象主題:
public interface ISong{
//公共抽象方法
void Sing();
}
真實主題:
//目標對象
public class JJ implements ISong{
@Override
public void Sing() {
System.out.println("林俊杰在唱歌....");
}
}
代理主題:
//代理對象
public class JJProxy implements ISong{
//持有目標對象的引用!
private JJ jj;
public JJProxy(JJ jj) {
super();
this.jj = jj;
}
@Override
public void Sing() {
System.out.println("林俊杰唱歌前的準備工作");
jj.Sing();
System.out.println("林俊杰唱歌后的收拾工作");
}
}
客戶端測試:
public class Client {
public static void main(String[] args) {
ISong iSong; //面向接口編程:依賴倒轉原則
iSong = new JJProxy(new JJ());
iSong.Sing();
/**
控制檯輸出:
林俊杰唱歌前的準備工作
林俊杰在唱歌....
林俊杰唱歌后的收拾工作
*/
}
}
優點:可以做到在不修改目標對象的功能前提下,對目標功能擴展.
缺點: 因爲代理對象需要與目標對象實現一樣的接口,所以會有很多代理類,類太多.同時,一旦接口增加方法,目標對象與代理對象都要維護.
而動態代理方式可以解決上面的問題
一:代理模式目的:就是爲了對某個對象的增強
增強對象的手段
1.繼承:被增強對象不能變,增強內容不能變
2.裝飾者模式:被增強對象可變,增強內容不可變
3.動態代理:被增強對象可變,增強內容可變
所有使用裝飾者模式的地方,都可以使用代理模式!
二:概述:
今天來學習一下代理模式,學習動態代理模式一般都是在框架中使用,如果不寫框架,基本上使用不到,但爲什麼還要去學呢?目的是爲了更好的理解框架內部的原理,strust1,struts2,spring中都有用到!
今天其實就是學習一下JDK中的動態代理(Proxy)
Object proxyObj = Proxy.newProxyInstance(ClassLoader loader,class[] interfaces,InvocationHandler h);
newProxyInstance的三個參數:
ClassLoader :把.class文件加載到內存,形成Class對象,怎麼獲取呢? Class對象裏面有個getClassLoader(),所以先得到Class對象就行了
class[]:指定的接口數組
InvocationHandler :它是一個接口!裏面只有一個invoke()方法,它的名字叫調用處理器,
其實無論你調用代理對象的什麼方法,它都是在調用InvocationHandler的invoke()方法!
這個代碼的含義:
在運行時動態的創建一組指定接口的實現類對象!(創建對象)
也就是說proxyObj 就是一個實現了interfaces裏面的所有接口的實現類,至於它到底是什麼類型我們不知道,我們只知道他是指定接口的實現類,而且它不會有.class文件,因爲它是在運行的時候創建的!
示例代碼:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
interface A{
void a();
}
interface B{
void b();
}
public class Client {
public static void main(String[] args) {
ClassLoader classLoader = Client.class.getClassLoader();
//這個proxyObj代理對象,就是實現了A和B接口!
Object proxyObj = Proxy.newProxyInstance(classLoader, new Class[]{A.class,B.class},new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("你好動態代理");
return null;
}
});
/**
* 既然說proxyObj代理對象是實現了A,B兩個接口
* 疑問一:那麼我想知道實現類具體是什麼類型的!
* 疑問二:那麼應該可以使用A,B接口中的方法
*/
System.out.println(proxyObj.getClass().getSimpleName());//$Proxy0,不知道什麼類型,也不用管
System.out.println(proxyObj instanceof A); //true
System.out.println(proxyObj instanceof B); //true
//直接調用是Object類中的方法,所以我得強轉一下,不管調用代理對象中的什麼方法,都會去執行InvocationHandler中的invoke()方法!, 除了getClass();
A a = (A)proxyObj;
a.a();
a.toString();
}
}
invoke方法中的三個參數!
invoke(Object proxy, Method method, Object[] args)
proxy:表示代理對象
method:表示當前被調用方法的反射對象
args:表示當前被調用方法的參數
三:實例
目標對象:就是需要被增強的對象
代理對象:就是需要維護一個目標對象的引用,然後在目標對象上增強的對象
目標方法:增強的內容
代理對象 = 目標對象 + 增強!
動態代理中的角色:抽象主題,真實主題,代理主題
抽象主題
public interface Waiter {
void server();
}
真實主題
public class ManWaiter implements Waiter{
@Override
public void server() {
System.out.println("服務中....");
}
}
代理主題和測試代碼!
public class Client {
public static void main(String[] args) {
Waiter waiter = new ManWaiter();//目標對象
ClassLoader classLoader = Client.class.getClassLoader();
Class[] interfaces = {Waiter.class};
InvocationHandler h = new WaiterInvocationHandler(waiter);
//代理對象:就是在目標對象的基礎上進行增強了的對象!
Object proxyObj = Proxy.newProxyInstance(classLoader, interfaces,h);
//強轉一下才能使用接口中的方法
Waiter w = (Waiter)proxyObj;
w.server();
}
}
class WaiterInvocationHandler implements InvocationHandler{
//維護一個目標對象的引用
private Waiter waiter;
public WaiterInvocationHandler(Waiter waiter) {
super();
this.waiter = waiter;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("你好");
this.waiter.server();
System.out.println("再見");
return null;
}
}
還有一個cglib代理:暫時不寫