JDK的動態代理

JDK的動態代理java

1、靜態代理編程

瞭解動態代理前,有必要先講解下靜態代理。設計模式

舉個例子:銀行開通了短信業務,在你取錢,存錢,轉帳後都會 給你發送短信,咱們來模擬下業務場景。框架

靜態代理的實現ide

下面來模擬下業務代碼函數

1.定義IBankCardService接口this

/**
 * 銀行卡操做接口
 * @author yizl
 *
 */
public interface IBankCardService {
    
    /**
     * 存錢
     * @param cardId
     */
    public void putInMoney(String cardId);
    
    /**
     * 取錢
     * @param cardId
     */
    public void outMoney(String cardId);
    
    /**
     * 查詢餘額
     * @param cardId
     */
    public String getMoney(String cardId);
}

2.接口實現(BankCardServiceImpl)spa

/**
 * 銀行卡操做實現類
 * @author yizl
 *
 */
public class BankCardServiceImpl implements IBankCardService {

    @Override
    public void putInMoney(String cardId) {
        System.out.println("開始往銀行卡帳號爲:"+cardId+" 存錢");
    }

    @Override
    public void outMoney(String cardId) {
        System.out.println("向銀行卡帳號爲:"+cardId+" 取錢");
    }

    @Override
    public String getMoney(String cardId) {
        System.out.println("查詢銀行卡帳號爲:"+cardId+" 的餘額");
        return null;
    }

}

3.編寫代理類設計

假設項目經理有個需求:在每次業務操做後都須要向用戶發送短信.代理

在不修改已有的實現類的前提下怎麼實現這個需求.

1.咱們寫一個代理類,讓它與銀行卡操做實現類的接口相同.

2.在代理類的構造器中,傳入銀行卡操做實現類,在代理類的方法內部仍然調用銀行卡操做實現類的方法.

圖片描述

代理類

/**
 * 代理銀行卡操做實現類
 * @author yizl
 *
 */
public class ProxyBankCardServiceImpl implements IBankCardService {

    private IBankCardService bankCardService;
    
    public ProxyBankCardServiceImpl(IBankCardService bankCardService) {
        this.bankCardService=bankCardService;
    }

    @Override
    public void putInMoney(String cardId) {
        bankCardService.putInMoney(cardId);
        System.out.println("向客戶發送短信");
    }

    @Override
    public void outMoney(String cardId) {
        bankCardService.outMoney(cardId);
        System.out.println("向客戶發送短信");
    }

    @Override
    public String getMoney(String cardId) {
        bankCardService.getMoney(cardId);
        System.out.println("向客戶發送短信");
        return null;
    }
    
}

4.調用代理類

public class ProxyTest {

    public static void main(String[] args) {
        IBankCardService bankCardService =new BankCardServiceImpl();
        IBankCardService proxyBankCard=new ProxyBankCardServiceImpl(bankCardService);
        proxyBankCard.putInMoney("9527");
    }
    
}

打印結果:
    開始往銀行卡帳號爲:9527的帳戶存錢
    向客戶發送短信

能夠看出,代理類的做用:代理對象=加強代碼+目標對象

代理類只對銀行卡操做實現類進行加強,每一個方法都添加發送短信業務,真正業務仍是在銀行卡操做實現類中在進行。

靜態代理的缺點

咱們發現靜態代碼其實很麻煩,有點脫褲子放屁的意思.

靜態代理的缺點:

1.要爲每個目標類都要編寫相應的代理類,會有不少代理類。

2.接口改了,目標類和代理類都要跟着改。

2、動態代理

咱們只想寫加強的代碼,不須要寫代理類,加強代碼還能夠複用到不一樣的目標類。這時動態代理橫空出世了。

動態代理實現

一、獲取代理類方式一

1.JDK提供了 java.lang.reflect.Proxy類有一個getProxyClass(ClassLoader, interfaces)靜態方法,傳入類加載器,和接口,就能夠獲得代理類的Class對象.

2.獲得了代理類的class對象,經過代理類的class對象獲得構造器,java.lang.reflect.InvocationHandler類中,每個動態代理類都要實現InvocationHandler接口,動態代理對象調用一個方法時,就會轉到實現InvocationHandler接口類的invoke方法.

3.獲得代理類,實行調用.

public class ProxyTest {

    public static void main(String[] args) throws Exception {
        //目標對象
        IBankCardService bankCard=new BankCardServiceImpl();
        //獲取代理對象
        IBankCardService proxyBank = (IBankCardService) getProxy(bankCard);
        //調用方法
        proxyBank.getMoney("9527");
    }
    
    /**
     * 獲取代理類
     * @param target 目標類
     * @return
     * @throws SecurityException 
     * @throws NoSuchMethodException 
     */
    private static Object getProxy(Object target) throws Exception {
        //獲得代理類大class
        Class proxyClass = Proxy.getProxyClass(target.getClass().getClassLoader(),     target.getClass().getInterfaces());
        //建立代理類的構造函數,構造函數的方法必須傳入InvocationHandler接口的實現類
        Constructor constructor=proxyClass.getConstructor(InvocationHandler.class);
        //獲取代理類
        Object proxy =constructor.newInstance(new InvocationHandler() {
            
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //調用目標文件的方法
                Object resulet = method.invoke(target,args);
                //加強方法
                System.out.println("向客戶發送短信");
                return resulet;
            }
            
        });
        return proxy;
    }
}

打印結果:
    查詢銀行卡帳號爲:9527的帳戶 的餘額
    向客戶發送短信

二、獲取代理類方式二

實際變成中不會使用getProxyClass(),由於JDK的Proxy類提供了更好用的方法newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h),直接傳入InvocationHandler 實現類就能夠的到代理類.

1.代理類的調用處理程序實現

/**
 * 發送短信調用類
 * @author yizl
 *
 */
public class SendMessageInvocation implements InvocationHandler {
    /**
     * 目標類    
     */
    private Object obj;
    
    /**
     * 經過構造方法傳參
     * @param obj
     */
    public SendMessageInvocation(Object obj) {
        this.obj=obj;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //調用目標文件的方法
        Object resulet = method.invoke(obj,args);
        //加強方法
        System.out.println("向客戶發送短信");
        return resulet;

    }
}

2.獲取代理類,調用取錢方法

public class ProxyTest {
    public static void main(String[] args) throws Exception {
        // 獲取銀行卡操做實現類
        IBankCardService bankCard = new BankCardServiceImpl();
        // 獲取銀行卡操做類的代理類
        IBankCardService proxyBank = (IBankCardService)Proxy.newProxyInstance(bankCard.getClass().getClassLoader(),
        bankCard.getClass().getInterfaces(),new SendMessageInvocation(bankCard));
        proxyBank.outMoney("9527");
    }
}

打印結果:
    向銀行卡帳號爲:9527的帳戶取錢
    向客戶發送短信

用JDK提供的代理類,很完美的解決了,不寫代理類,直接寫加強方法,直接就獲取到目標的代理類。

3、動態代理的應用

設計模式中有一個設計原則是開閉原則:軟件中對於擴展是開放的,對於修改是封閉的。再不改變源碼的狀況下,拓展它的行爲。

工做中接收了不少之前的代碼,裏面的邏輯讓人摸不透,就可使用代理類進行加強。

Spring的AOP就是Java的動態代理來實現的切面編程。

RPC框架,框架自己不知道要調用哪些接口,哪些方法。這是框架能夠一個建立代理類給客戶端使用。

實際開發中的,通用異常處理,通用日誌處理,事物處理均可以用到動態代理。

4、總結

優勢:

動態代理類簡化了代碼編程工做,提升了軟件的可擴展性。

缺點:

JDK動態代理只能代理有接口的實現類,沒有接口的類就不能用JDK的動態代理。(Cglib動態代理能夠對沒有接口的類實現代理)
相關文章
相關標籤/搜索