動態代理方式
動態代理:
特點:字節碼隨用隨創建,隨用隨加載
作用:不修改源碼的基礎上對方法增強
分類:
基於接口的動態代理
基於子類的動態代理
一、基於接口的動態代理
基於接口的動態代理:
涉及的類:Proxy
提供者:JDK官方
如何創建代理對象:
使用Proxy類中的newProxyInstance方法
創建代理對象的要求:
被代理類最少實現一個接口,如果沒有則不能使用
newProxyInstance方法的參數:
ClassLoader:類加載器
它是用於加載代理對象字節碼的。和被代理對象使用相同的類加載器。
固定寫法:
代理是誰就寫誰的 XXX.getClass().getClassLoader()
Class[]:字節碼數組
它是用於讓代理對象和被代理對象有相同的方法
固定寫法:
代理誰就寫誰的 XXX.getClass().getInterfaces()
InvocationHandler:
他是讓我們寫如何代理。一般是寫一個該接口的實現類,通常情況下都是匿名內部類,但不是必須的
此接口的實現類都是誰用誰寫。
注意:如果我們的類不實現任何接口的時候,執行Client會報代理異常
不使用任何接口,proxy這種代理無法使用
舉例:賣東西
前提:有一個接口IProducer,一個實現類Producer(最後我會附上這部分的代碼(“動態代理介紹”)
IProducer proxyProducer爲動態代理對象,方法執行調用它
newProxyInstance方法
被代理類最少實現一個接口,如果沒有則不能使用
前兩步都是一個套路
1.Class 加載代理對象
代理XXX就寫XXX.getClass().getClassLoader()
2.Class[]
讓代理對象和被代理對象有相同的方法
代理XXX就寫XXX.getClass().getClassInterfaces()
下一步不同了
3.InvocationHandler 調用處理程序
有下面的方法
這裏是Object類型,所以我們創建代理對象的時候記得強轉一下
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {}
* @param proxy 代理對象的引用
* @param method 當前執行的方法
* @param args 當前執行方法所需要的參數
* @return 和被代理對象方法有相同的返回值
執行方法的一般寫法
Object returnValue = null;
returnValue=執行方法;
return returnValue;
再正常執行方法
public class Client {
public static void main(String[] args) {
//匿名類訪問外部成員變量的時候,要求是最終的final
//彈幕:局部變量隨着方法的調用而調用,隨着方法消失而消失,而對內存的內容不會立即消失,還會繼續引用局部變量
final Producer producer = new Producer();
//返回的是Object類型 需要強轉一下
IProducer proxyProducer = (IProducer) Proxy.newProxyInstance(producer.getClass().getClassLoader(),
producer.getClass().getInterfaces(),
new InvocationHandler() {
/**匿名內部類
*
* 作用:執行被代理對象的任何接口方法都會經過該方法
* 方法參數的含義
* @param proxy 代理對象的引用
* @param method 當前執行的方法
* @param args 當前執行方法所需要的參數
* @return 和被代理對象方法有相同的返回值
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object returnValue = null;
//1.獲取方法執行的參數
Float money = (Float) args[0];
//2.判斷當前方法是不是銷售
if ("saleProduct".equals(method.getName())) {
//執行操作
returnValue = method.invoke(producer, money * 0.8f);
}
return returnValue;
}
});
proxyProducer.saleProduct(1000f);
二、基於子類的動態代理
基於子類的動態代理:
涉及的類:Enhancer
提供者:第三方cglib庫
如何創建代理對象:
使用Enhancer類中的create方法
創建代理對象的要求:
被代理類不能是最終類
create方法的參數:
Class:字節碼
它是用於指定被代理對象的字節碼。
XXX.getClass().getClassLoader();
Callback:用於提供增強的代碼(和
它是讓我們寫如何代理。我們一般都是些一個該接口的實現類,通常情況下都是匿名內部類,但不是必須的。
此接口的實現類都是誰用誰寫。
我們一般寫的都是該接口的子接口實現類:MethodInterceptor 方法攔截
舉例:賣東西
前提:有一個接口IProducer,一個實現類Producer(最後我會附上這部分的代碼(“動態代理介紹”)
Producer cglibProducer爲動態代理對象,方法執行調用它
被代理類不能是最終類(不能是final)
create方法
1.Class加載代理對象
代理XXX就寫XXX.getClass().getClassLoader()
2.Callback:用於提供增強的代碼
一般寫的都是該接口的子接口實現類
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {}
* 執行被代理對象的任何方法都會經過該方法
* @param proxy
* @param method
* @param args
* 以上三個參數和基於接口的動態代理中invoke方法的參數是一樣的
* @param methodProxy:當前執行方法的代理對象(一般用不上8
執行方法的一般寫法
Object returnValue = null;
returnValue=執行方法;
return returnValue;
再正常執行方法
public class Client {
public static void main(String[] args) {
//匿名類訪問外部成員變量的時候,要求是最終的final
final Producer producer = new Producer();
Producer cglibProducer=(Producer)Enhancer.create(producer.getClass(), new MethodInterceptor() {
/**
* 執行被代理對象的任何方法都會經過該方法
* @param proxy
* @param method
* @param args
* 以上三個參數和基於接口的動態代理中invoke方法的參數是一樣的
* @param methodProxy:當前執行方法的代理對象
* @return
* @throws Throwable
*/
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object returnValue = null;
//1.獲取方法執行的參數
Float money = (Float) args[0];
//2.判斷當前方法是不是銷售
if ("saleProduct".equals(method.getName())) {
returnValue = method.invoke(producer, money * 0.8f);
}
return returnValue;
}
});
cglibProducer.saleProduct(1000f);
三、源碼及模塊結構
IProducer接口
public interface IProducer {
/**
* 銷售
* @param money
*/
public void saleProduct(float money);
}
Producer類
/**
* 一個生產者
*/
public class Producer implements IProducer {
/**
* 銷售
* @param money
*/
public void saleProduct(float money){
System.out.println("銷售產品,並拿到錢:"+money);
}
}
Proxy裏的Client(執行方法測試
public class Client {
public static void main(String[] args) {
final Producer producer = new Producer();
//返回的是Object類型 需要強轉一下
IProducer proxyProducer = (IProducer) Proxy.newProxyInstance(producer.getClass().getClassLoader(),
producer.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object returnValue = null;
//1.獲取方法執行的參數
Float money = (Float) args[0];
//2.判斷當前方法是不是銷售
if ("saleProduct".equals(method.getName())) {
//執行操作
returnValue = method.invoke(producer, money * 0.8f);
}
return returnValue;
}
});
proxyProducer.saleProduct(1000f);
}
}