一.前沿
為了讓我們更好的理解動態代理,讓我們先來了解一下靜態代理,
靜態代理的特點是:代理類和被代理類在編譯期間,就確定下來了,
先直接上代碼:
//代理類與被代理類需要實作的介面
interface ClothFactory{
void produceCloth();
}
//代理類
class ProxyClothFactory implements ClothFactory{
public ClothFactory getFactory() {
return factory;
}
public void setFactory(ClothFactory factory) {
this.factory = factory;
}
//用被代理類物件進行實體化
private ClothFactory factory;
public ProxyClothFactory(ClothFactory factory){
this.factory = factory;
}
@Override
public void produceCloth() {
//代理類做一些前期準備作業,這里直接用輸出陳述句簡寫
System.out.println("代理類做一些準備作業-------------");
//呼叫被代理類
factory.produceCloth();
//代理類做一些后期收尾作業,這里直接用輸出陳述句簡寫
System.out.println("代理類做一些收尾作業-------------");
}
}
//被代理類0
class PumaClothFactory implements ClothFactory{
@Override
public void produceCloth() {
System.out.println("Puma工廠正在生產服裝!");
}
}
//被代理類1
class AdidasClothFactory implements ClothFactory{
@Override
public void produceCloth() {
System.out.println("Adidas工廠正在生產服裝!");
}
}
public class StaticProxyTest {
public static void main(String[] args) {
//創建被代理類0的物件
PumaClothFactory puma = new PumaClothFactory();
//創建代理類的物件,在構造起中傳入被代理類作為實參
ProxyClothFactory proxyClothFactory = new ProxyClothFactory(puma);
//呼叫代理類物件的produceCloth()方法,
// 實際上在代理類物件的produceCloth()方法中呼叫了被代理類物件的produceCloth()方法,實作靜態代理
proxyClothFactory.produceCloth();
System.out.println();
//創建被代理類1的物件
AdidasClothFactory adidas = new AdidasClothFactory();
//把被代理類物件改為adidas
proxyClothFactory.setFactory(adidas);
//呼叫代理類物件的produceCloth()方法
proxyClothFactory.produceCloth();
}
}
輸出結果:
代理類做一些準備作業-------------
Puma工廠正在生產服裝!
代理類做一些收尾作業-------------
代理類做一些準備作業-------------
Adidas工廠正在生產服裝!
代理類做一些收尾作業-------------
Process finished with exit code 0
分析:
上述代碼中代理類與被代理類都實作了同一個介面ClothFactory;
在被代理類中實作了介面的produceCloth()方法;
在代理類中宣告了被代理類物件作為其屬性,然后又在代理類的有參構造起中對其屬性進行賦值;
在代理類中同樣也實作了produceCloth()方法,但是在該方法中還通過其屬性factory呼叫了被代理類的produceCloth()方法;
從而達到了靜態代理的效果,
ps:被代理類也可以為實作了ClothFactory介面的其他類,如AdidasClothFactory,
二.動態代理
與靜態代理相比,動態代理需要解決的兩個問題:
1.如何根據加載到記憶體中的被代理類,動態的創建一個代理類及其物件,
2.當通過代理類的物件呼叫方法時,如何動態的去呼叫被代理類中的同名方法,
ps:也就是說代理類的物件是根據被代理類的物件動態生成的,
直接上代碼:(詳情看代碼中的注釋)
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @Author: YuShiwen
* @Date: 2020/11/5 3:56 PM
* @Version: 1.0
*/
/*
* 與靜態代理相比,動態代理需要解決的兩個問題:
* 問題一:如何根據加載到記憶體中的被代理類,動態的創建一個代理類及其物件,
* 問題二:當通過代理類的物件呼叫方法時,如何動態的去呼叫被代理類中的同名方法,
* */
interface ShoesFactory{
void produceShoes();
void produceCasualShoes();
}
//被代理類
class PumaShoesFactory implements ShoesFactory{
@Override
public void produceShoes() {
System.out.println("Puma工廠開始生產運動鞋!");
}
@Override
public void produceCasualShoes() {
System.out.println("Puma工廠開始生產休閑鞋!");
}
}
/************************根據被代理類動態生產代理類************************/
class ProxyFactory{
//呼叫此方法回傳一個代理類的物件,解決問題一;
//即根據傳入的被代理類物件,動態的創建一個代理類和其物件
public static Object getProxyInstance(Object obj){//obj:被代理類的物件
MyInvocationHandler handler = new MyInvocationHandler();
handler.bind(obj);
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler);
}
}
//
class MyInvocationHandler implements InvocationHandler{
private Object obj;//賦值時需要使用被代理類的物件進行賦值
public void bind(Object obj){
this.obj = obj;
}
//當我們通過代理類的物件呼叫方法a時,就會自動呼叫如下的invoke方法
//將被代理類要執行的方法a的功能就宣告在invoke()中,解決問題二,
@Override
// 第一個引數proxy為代理類的物件;
// 第二個引數method是代理類物件調的是什么方法,這里就是什么方法;
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//method:即為代理類物件呼叫的方法,此方法也就作為了被代理類物件要呼叫的方法,
//obj:被代理類的物件
Object value = method.invoke(obj, args);
//上述方法的回傳值就作為當前類中的invoke()方法的回傳值
return value;
}
}
/************************根據被代理類動態生產代理類************************/
public class DynamicProxyTest {
public static void main(String[] args) {
PumaShoesFactory pumaShoesFactory = new PumaShoesFactory();
//proxyInstance為代理類的物件
ShoesFactory proxyInstance = (ShoesFactory) ProxyFactory.getProxyInstance(pumaShoesFactory);
//當通過代理類物件呼叫方法時,會自動的呼叫被代理類中的同名的方法,
proxyInstance.produceShoes();
proxyInstance.produceCasualShoes();
System.out.println("************下面用前面提到的靜態代理類作為引數**************");
PumaClothFactory pumaClothFactory = new PumaClothFactory();
ClothFactory proxyInstance1 = (ClothFactory) ProxyFactory.getProxyInstance(pumaClothFactory);
proxyInstance1.produceCloth();
AdidasClothFactory adidasClothFactory = new AdidasClothFactory();
ClothFactory proxyInstance2 = (ClothFactory) ProxyFactory.getProxyInstance(adidasClothFactory);
proxyInstance2.produceCloth();
}
}
運行結果:
Puma工廠開始生產運動鞋!
Puma工廠開始生產休閑鞋!
************下面用前面提到的靜態代理類作為引數**************
Puma工廠正在生產服裝!
Adidas工廠正在生產服裝!
Process finished with exit code 0
三.動態代理添加通用方法
可以看到上述動態代理中,呼叫生產鞋的方法和生產服裝的方法時沒有做生產前的處理和生產后的處理,下面我們將創建一個類,在該類中添加兩個方法作為其共用方法,
在上述二的代碼基礎上添加如下類:
//如下類中的方法作為共用的方法
class ProxyFactoryUtil{
public void method0(){
System.out.println("++++++++++++工廠生產產品前期做準備+++++++++++");
}
public void method1(){
System.out.println("++++++++++++工廠生產產品后期做準備+++++++++++");
}
}
更改MyInvocationHandler類,改后代碼如下:
class MyInvocationHandler implements InvocationHandler{
private Object obj;//賦值時需要使用被代理類的物件進行賦值
public void bind(Object obj){
this.obj = obj;
}
//當我們通過代理類的物件呼叫方法a時,就會自動呼叫如下的invoke方法
//將被代理類要執行的方法a的功能就宣告在invoke()中,解決問題二,
@Override
// 第一個引數proxy為代理類的物件;
// 第二個引數method是代理類物件調的是什么方法,這里就是什么方法;
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
ProxyFactoryUtil util = new ProxyFactoryUtil();
util.method0();
//method:即為代理類物件呼叫的方法,此方法也就作為了被代理類物件要呼叫的方法,
//obj:被代理類的物件
Object value = method.invoke(obj, args);
util.method1();
//上述方法的回傳值就作為當前類中的invoke()方法的回傳值
return value;
}
}
運行結果:
++++++++++++工廠生產產品前期做準備+++++++++++
Puma工廠開始生產運動鞋!
++++++++++++工廠生產產品后期做準備+++++++++++
++++++++++++工廠生產產品前期做準備+++++++++++
Puma工廠開始生產休閑鞋!
++++++++++++工廠生產產品后期做準備+++++++++++
************下面用前面提到的靜態代理類作為引數**************
++++++++++++工廠生產產品前期做準備+++++++++++
Puma工廠正在生產服裝!
++++++++++++工廠生產產品后期做準備+++++++++++
++++++++++++工廠生產產品前期做準備+++++++++++
Adidas工廠正在生產服裝!
++++++++++++工廠生產產品后期做準備+++++++++++
Process finished with exit code 0
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/208903.html
標籤:其他
上一篇:物件 方法
