Java代理模式之動態代理
代理模式是設計模式中非常重要的一種型別。代理模式從型別上來說,可以分為靜態代理和動態代理兩種型別。
假設一個場景,有一個蛋糕店,賣的蛋糕都是用蛋糕機做的,而且不同種類的蛋糕由不同的蛋糕機來做,有水果蛋糕機,巧克力蛋糕機等。它們賣的麵包片也是麵包機做的,不同種類的麵包機也是由不同的麵包機來做,有紅豆麵包機,葡萄乾麵包機等。用程式碼描述如下。
//做蛋糕的機器 public interface CakeMachine{ void makeCake(); } //專門做水果蛋糕的機器 class FruitCakeMachine implements CakeMachine{ public void makeCake() { System.out.println("Making a fruit cake..."); } } //專門做巧克力蛋糕的機器 public class ChocolateCakeMachine implements CakeMachine{ public void makeCake() { System.out.printf("making a Chocolate Cake..."); } } //做麵包的機器 public interface BreadMachine { void makeBread(); } //專門做紅豆麵包的機器 public class RedBeanBreadMachine implements BreadMachine { public void makeBread() { System.out.println("making red bean bread...."); } } //專門做葡萄乾麵包的機器 public class CurrantBreadMachine implements BreadMachine{ public void makeBread() { System.out.println("making currant bread..."); } } //蛋糕店 public class CakeShop { public static void main(String[] args) { new FruitCakeMachine().makeCake(); new ChocolateCakeMachine().makeCake(); new RedBeanBreadMachine().makeBread(); new CurrantBreadMachine().makeBread(); } } }
如果有個客戶想要在水果蛋糕上面加珍珠,我們可以再做一個水果珍珠蛋糕機,但是這樣成本太高,因為不能因為一個客戶的請求就造一個新蛋糕機。根據修改封閉,擴充套件開放原則。我們可以設計一個珍珠代理類(BoboCakeProxy),先用水果蛋糕機做一個水果蛋糕,然後再加上珍珠就好了。
靜態代理:
public class BoboCakeProxy implements CakeMachine{ private CakeMachine cakemachine; public BoboCakeProxy(CakeMachine cakemachine){ this.cakemachine=cakemachine; } @Override public void makeCake() { cakemachine.makeCake(); System.out.println("add bobo"); } } //蛋糕店 public class CakeShop { public static void main(String[] args) { //可以給各種各樣的蛋糕加上杏仁 FruitCakeMachine fruitCakeMachine = new FruitCakeMachine(); BoboCakeProxy boboCakeProxy = new BoboCakeProxy(fruitCakeMachine); boboCakeProxy.makeCake(); boboCakeProxy = new BoboCakeProxy(new ChocolateCakeMachine()); boboCakeProxy.makeCake(); } }
動態代理:
我們只能為一種產品加上珍珠,如果要給麵包加珍珠,就需要重新寫一個珍珠麵包代理,如果產品有100種,那麼就得寫100個代理類。所以要用動態代理,可以讓我們只寫一次實現,但是任何型別的產品都可以使用。InvocationHandler。動態代理和靜態代理的區別就是靜態代理只能針對特定一種產品做代理動作,而動態代理可以針對所有型別產品做代理。寫一個珍珠動態代理。
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class BoboHandler implements InvocationHandler{ private Object object; public BoboHandler(Object object){ this.object=object; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result=method.invoke(object, args);//呼叫真正的蛋糕機做蛋糕 System.out.println("add bobo"); return result; } }
蛋糕店開始工作
public class CakeShop {
public static void main(String[] args) {
//動態代理(可以同時給蛋糕、麵包等加珍珠)
//給蛋糕加上珍珠
FruitCakeMachine fruitCakeMachine = new FruitCakeMachine();
BoboHandler boboHandler = new BoboHandler(fruitCakeMachine);
CakeMachine cakeMachine = (CakeMachine) Proxy.newProxyInstance(fruitCakeMachine.getClass().getClassLoader(), fruitCakeMachine.getClass().getInterfaces(), boboHandler);
cakeMachine.makeCake();
//給麵包加上珍珠
RedBeanBreadMachine redBeanBreadMachine = new RedBeanBreadMachine();
boboHandler = new BoboHandler(redBeanBreadMachine);
BreadMachine breadMachine = (BreadMachine) Proxy.newProxyInstance(redBeanBreadMachine.getClass().getClassLoader(), redBeanBreadMachine.getClass().getInterfaces(), boboHandler);
breadMachine.makeBread();
}
}
}
靜態代理只能針對某一介面(麵包 或 蛋糕)進行操作,如果要對所有介面都(所有產品)都能做一樣操作,那就必須要動態代理出馬了。
動態代理的幾種實現方式
-
Java動態代理
-
CGLib
Java動態代理適合有介面抽象的類代理,而CGLib適合沒有介面物件的類代理。
CGLib實現方式
Java動態代理只能針對有介面的類進行擴充套件,CGLib是使用繼承原有類的方式來實現代理的。要是用CGLib來實現以上功能就是,先寫一個珍珠攔截器類。
public class BoboInterceptor implements methodInterceptor{
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
methodProxy.invokeSuper(o, objects);
System.out.println("adding bobo...");
return o;
}
}
然後蛋糕店通過呼叫攔截器類來進行加珍珠操作。
public class CakeShop {
public static void main(String[] args) {
//CGLib動態代理(可以同時給蛋糕、麵包等加杏仁)
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(FruitCakeMachine.class);
enhancer.setCallback(new BoboInterceptor());
FruitCakeMachine fruitCakeMachine = (FruitCakeMachine) enhancer.create();
fruitCakeMachine.makeCake();
}
}
}
enhancer.setSuperClass()設定需要增強的類,enhancer.setCallback()設定需要回調的攔截器,也就是實現了MethodInterceptor的介面的類。最後使用enhancer.create()生成了對應的增強類。