一、動態代理的特點
字節碼隨用隨創建,隨用隨加載。
它與靜態代理的區別也在於此,因爲靜態代理是字節碼一上來就創建好,並完成加載
裝飾者模式就是靜態代理的一種體現。
(1) 基於接口的動態代理:
提供者: JDK 官方的 Proxy 類。
要求:被代理類最少實現一個接口。
(2)基於子類的動態代理
提供者:第三方的 CGLib,如果報 asmxxxx 異常,需要導入 asm.jar
。
要求:被代理類不能用 final 修飾的類(最終類)。
二、基於接口的動態代理
案例:在很久以前,演員和劇組都是直接見面聯繫的。沒有中間人環節。而隨着時間的推移,產生了一個新興職業:經紀人(中間人),這個時候劇組再想找演員就需要通過經紀人來找了。下面我們就用代碼演示出來。
創建代理對象:使用Proxy
類中的newProxyInstance
方法
創建代理對象的要求:被代理類最少實現一個接口,如果沒有則不能使用
newProxyInstance方法的參數:
* ClassLoader 類加載器:它是用於加載代理對象字節碼的,和被代理對象使用相同的類加載器,固定寫法。
* Class[] 字節碼數組:它是用於讓代理對象和被代理對象有相同方法,固定寫法。
* InvocationHandler:用於提供增強的代碼
* 它是讓我們寫如何代理。我們一般都是些一個該接口的實現類,通常情況下都是匿名內部類,但不是必須的。
* 此接口的實現類都是誰用誰寫。
(1)演員類
/**
* 一個演員
* 實現了接口,就表示具有接口中的方法實現。即:符合經紀公司的要求
*/
public class Actor implements IActor {
@Override
public void basicAct(float money) {
System.out.println("拿到錢,開始基本的表演:" + money);
}
@Override
public void dangerAct(float money) {
System.out.println("拿到錢,開始危險的表演:" + money);
}
}
(2)經紀公司接口
/**
* 一個經紀公司的要求:
* 能做基本的表演和危險的表演
*/
public interface IActor {
/**
* 基本演出
* @param money
*/
public void basicAct(float money);
/**
* 危險演出
* @param money
*/
public void dangerAct(float money);
}
(3)模擬消費者
public class Client {
public static void main(String[] args) {
//一個劇組直接找演員
final Actor actor = new Actor();
//創建代理對象的要求:被代理類最少實現一個接口,如果沒有則不能使用
IActor proxyActor = (IActor) Proxy.newProxyInstance(actor.getClass().getClassLoader(),
actor.getClass().getInterfaces(),
new InvocationHandler() {
/**
* 執行被代理對象的任何方法,都會經過該方法。
* 此方法有攔截的功能。
*
* 參數:
* proxy:代理對象的引用。不一定每次都用得到
* method:當前執行的方法對象
* args:執行方法所需的參數
* 返回值:
* 當前執行方法的返回值
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getName();
Float money = (Float) args[0];
Object rtValue = null;
if ("dangerAct".equals(name)) {
//危險演出,沒有5000不演
if (money > 5000) {
//劇組扣除一半,演員到手一半,這裏實際對方法進行了增強
rtValue = method.invoke(actor, money / 2);
}
}
return rtValue;
}
});
//1.沒有經紀公司,直接找演員
actor.dangerAct(5000f);
//2.通過經紀公司聯繫演員
proxyActor.dangerAct(50000f);
}
}
三、基於子類的動態代理
涉及的類:Enhancer
提供者:第三方cglib庫
<!--實現基於子類的動態代理的cglib庫-->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.1_3</version>
</dependency>
如何創建代理對象:
* 使用Enhancer類中的create方法
* 創建代理對象的要求:被代理類不能是最終類
* create方法的參數:
* Class:字節碼,它是用於指定被代理對象的字節碼。
* Callback:用於提供增強的代碼,它是讓我們寫如何代理。
* 我們一般都是些一個該接口的實現類,通常情況下都是匿名內部類,但不是必須的。
* 此接口的實現類都是誰用誰寫。
* 我們一般寫的都是該接口的子接口實現類:MethodInterceptor
(1)Actor類不需要接口
/**
* 一個演員:此處未實現任何接口
*/
public class Actor {
public void basicAct(float money) {
System.out.println("拿到錢,開始基本的表演: " + money);
}
public void dangerAct(float money) {
System.out.println("拿到錢,開始危險的表演: " + money);
}
}
(2)
public class Client {
public static void main(String[] args) {
final Actor actor = new Actor();
/**
* 基於子類的動態代理
* 要求:被代理對象不能是最終類
* 用到的類:Enhancer
* 用到的方法:create(Class, Callback)
* 方法的參數:
* Class:被代理對象的字節碼
* Callback:如何代理
* @param args
*/
Actor cglibActor = (Actor) Enhancer.create(actor.getClass(),
new MethodInterceptor() {
/**
* 執行被代理對象的任何方法,都會經過該方法。在此方法內部就可以對被代理對象的任何方法進行增強。
* 參數:
* 前三個和基於接口的動態代理是一樣的。
* MethodProxy:當前執行方法的代理對象。
* 返回值:當前執行方法的返回值
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
String name = method.getName();
Float money = (Float) objects[0];
Object rtValue = null;
if("dangerAct".equals(name)){
//危險演出
if(money > 5000){
rtValue = method.invoke(actor, money/2);
}
}
return rtValue;
}
});
cglibActor.dangerAct(100000);
}
}