­

java動態代理

java動態代理

1. 簡介

java的動態代理功能是用來解決現有類功能不足,但我們又不想去修改現有類方法的問題,或者就是我們無法直接使用現有類的情況。它的實現方式有兩種,第一種是jdk自帶的動態代理功能,它的實現前提是現有類必須擁有一個接口,因為它是通過對現有類接口的實現來完成的。第二種方式是cglib,這是一個開源工具包,它的實現是通過繼承現有類,然後重寫現有類的方法實現的。它們在spring與mybatis框架中均有使用。學習它們的前提是你要對java的反射機制有一定的認知。本篇只介紹jdk原生的動態代理。

2. jdk動態代理

2.1 演示前景

客戶:購買u盤

代理商:淘寶

u盤工廠:金士頓,三星等

用戶想要購買u盤,是不可以直接去廠家購買的,需要通過淘寶等代理商進行購買,抽象成程序就是,u盤工程就是目標類,也就是現有類,淘寶就是代理類,客戶就是用戶類.用戶需要調用淘寶的方法進行購買u盤,而淘寶又需要調用工廠的方法進行購買.如果我們直接創建一個淘寶類,讓它去代理金士頓工廠,就會出現一個問題,那就是三星工廠由誰去代理,總不能再創建一個代理類吧,所以就可以使用動態代理的方式,在程序運行時期,根據不同的情況去創建一個合適的代理類

2.2編寫代碼

首先我們需要一個工廠的接口UsbFactory

package com.hzq.application.targetclass;

public interface UsbFactory {
    //售賣u盤,並返回實際價格的方法
    float sell(int num);
}

第二步就是創建一個UsbFactory的實現類UsbKingFactory代表金士頓工廠

package com.hzq.application.targetclass.impl;

import com.hzq.application.targetclass.UsbFactory;

public class UsbKingFactory implements UsbFactory {
    @Override
    public float sell(int num) {
        float onePrice = 30.0F;
        float price = onePrice * num;
        System.out.println("向廠家購買花費了"+price+"元");
        return price;
    }
}

現在需要我們代理的目標類已經好了,接下來就是重點了,jdk動態代理的實現方法(這個實現套路是固定的)。第三步,創建一個MyInvocationHandler類去實現接口InvocationHandler。這個接口只有一個需要我們去實現的方法,那就是invoke()方法,它有3個參數,第一個參數其實就是我們後面通過動態代理去創建的代理類,這個參數不需要我們的參與,不用去理會,第二個參數是我們要去增強的目標類的方法,第三個是該方法的參數.invoke內部就是我們要對目標類的方法增強的具體邏輯,也就是我們想怎樣去增強它,在本示例中我們是對用戶購買的商品收取20元中介費,然後送給用戶一張10元的優惠券

package com.hzq.application.proxy;

import com.hzq.application.targetclass.UsbFactory;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MyInvocationHandler implements InvocationHandler {
    private UsbFactory factory ;
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      //1.調用目標類的售賣方法,然後返回價格
        Float price = (Float) method.invoke(factory, args);
      //2.我們在之後進行自定義的增強,這裡我們是抽取20元的中介費用,然後送給客戶一張10元優惠券
        price += 20;
        System.out.println("代理商送您一張10元優惠券");
        System.out.println("客戶購買商品花費了"+price+"元");
        return price;
    }

    public MyInvocationHandler(UsbFactory factory) {
        this.factory = factory;
    }
}

最後一步就是去使用jdk動態代理來在運行期創建一個代理類對象了,在代碼中第第三步是重點,我們如果我們想要創建一個動態代理類,就必須要調用Proxy類的靜態方法newProxyInStance()方法,這個方法會返回給我們一個代理類對象,我們實際購買商品也是通過這個代理類來進行購買的。newProxyInstance()方法一共有三個參數,第一個參數是一個類加載器對象,類加載器對象可以通過目標類的Class對象去調用getClassLoader()方法去獲取,其實也可以使用其他你自定義的類的Class對象去獲取,因為最後獲得的都是同一個類加載器實例,但為了代碼的易讀性,就使用了目標類。第二個參數是目標類的接口類型,第三個參數就是我們自定義的MyInvocationHandler類的實例對象了,它裏面封裝這我們具體的增強邏輯代碼。方法調用完成後,如果沒有出現問題就會返回給我們一個代理類的實例,之後我們就可以去使用這個代理類的。

package com.hzq.application;

import com.hzq.application.proxy.MyInvocationHandler;
import com.hzq.application.targetclass.UsbFactory;
import com.hzq.application.targetclass.impl.UsbKingFactory;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class ProxyMain {
    public static void main(String[] args) {
      //1.實例化一個金士頓工廠對象
        UsbFactory usbFactory = new UsbKingFactory();
      //2.實例化一個MyInvocationHandler對象
        InvocationHandler handler = new MyInvocationHandler(usbFactory);
      //3.通過jdk動態代理創建出代理對象
        UsbFactory proxyInstance = (UsbFactory) Proxy.newProxyInstance(usbFactory.getClass().getClassLoader(),
                usbFactory.getClass().getInterfaces(),
                handler);
      //4.通過代理對象來購買商品
        float price = proxyInstance.sell(3);
        System.out.println(price);
    }
}

最後,當我們需要代理其他工廠去售賣u盤時,只需要將main方法中的第一步實例化的金士頓工廠改成其他品牌的工廠即可,當然前提是這個工廠的類實現了UsbFactory接口。

2.3 總結

當我們去使用jdk動態代理時,首先需要確保目標類實現了接口。之後的使用步驟就是:

  1. 實現InvocationHandler接口,將我們具體的增強邏輯代碼寫在invoke()方法中
  2. 使用Proxy類的newProxyInstance()方法去創建一個代理類的實例對象.
  3. 使用這個代理類對象
Tags: