1. 程式人生 > >java代理模式學習,靜態代理,JDK動態代理,CGLIB動態代理

java代理模式學習,靜態代理,JDK動態代理,CGLIB動態代理

                java代理模式學習,靜態代理,JDK動態代理,CGLIB動態代理

 

一、理解代理

1、代理,Proxy 。意思是:本來該你做的事兒,別人代替你去做。 比如說:即將十一到來,你要搶回家/旅遊的車票,可是你不能時時刻刻在電腦前一遍又一遍的刷著12306。 這時交給了 飛豬 等(此處省去若干個搶票軟體!)軟體幫助你搶。 飛豬就是你 搶票 這項工作的代理 。

2、代理作用: 日誌記錄,效能分析,解耦合。。。 自己百度去。

 

二、靜態代理 --- 程式碼來搶票

1、 定義一個搶票介面 Tickets

/**
* description: 搶票介面 --- 需要搶票需實現該介面
* @version v1.0
* @author w
* @date 2018年9月3日下午9:54:28
*/
public interface Tickets {
    void buyTickets();
}

2、定義一個User類 實現搶票介面,執行搶票操作

/**
* description: 定義一個User類 實現搶票介面,執行搶票操作
* @version v1.0
* @author w
* @date 2018年9月3日下午9:52:38
*/
public class User implements Tickets{
    private String name;

    public User(String name) {
        this.name = name;
    }

    @Override
    public void buyTickets() {
        System.out.println(name + " 開始搶票啦~ ");
    }
}

 

3、定義一個FliggyProxy類,實現搶票介面 --- 飛豬來幫你搶票啦

/**
* description: 飛豬 實現搶票介面 --- 飛豬來幫你搶票啦
* @version v1.0
* @author w
* @date 2018年9月3日下午9:58:44
*/
public class FliggyProxy implements Tickets {

    private User user ;

    public FliggyProxy(User user) {
        this.user = user;
    }

    @Override
    public void buyTickets() {
        System.out.println("充錢,享受1000M專線搶票 ");
        user.buyTickets();
        System.out.println("搶票成功. 廣告:沒有什麼是充錢解決不的問題.");
    }
}

4、定義一個 StaticProxyTest類,執行相關的測試操作

/**
* description: 靜態代理 test
* @version v1.0
* @author w
* @date 2018年9月3日下午10:01:26
*/
public class StaticProxyTest {

    @Test
    public void testOne(){
        // 建立一個使用者,小明 --- 一遍又一遍的刷著12306
        User user = new User("小明");
        user.buyTickets();
    }

    @Test
    public void testTwo(){
        // 建立一個需要搶票的使用者 --- 萬能小明
        User user = new User("萬能小明");
        // 小明開啟飛豬
        FliggyProxy fliggy = new FliggyProxy(user);
        // fliggy 開始搶票啦
        fliggy.buyTickets();
    }
}

5、執行結果:

testOne:小明 開始搶票啦~
testTwo:
充錢,享受1000M專線搶票
萬能小明 開始搶票啦~
搶票成功. 廣告:沒有什麼是充錢解決不的問題.

6、根據程式碼執行結果可以小明搶票的操作,交給飛豬後,很快就搶到了。 但是 飛豬的功能太單一,只能幫忙搶票。比如下次要搶月餅呢,再下次要搶手機呢? 要實現這些功能,我們就要寫n多個的 xxProxy。 作為程式設計師,總想著一勞永逸,能否只寫一個呢? 答案是有的,繼續往下。

 

三、JDK動態代理

1、定義一個 FliggyDynamic 類,實現 java.lang.reflect.InvocationHandler 介面 

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* description: JDK動態代理。 飛豬動態幫助無數人開啟搶票模式
* @version v1.0
* @author w
* @date 2018年9月3日下午10:06:21
*/
public class FliggyDynamic implements InvocationHandler {

    /**
    * 需要搶票的使用者 --- 目標代理物件
    */
    private Object target ;

    /**
    * 構造方法,初始化目標代理物件
    */
    public FliggyDynamic(Object target) {
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("充錢,享受1000M專線搶票 ");
        Object result = method.invoke(target, args);
        System.out.println("搶票成功. 廣告:沒有什麼是充錢解決不的問題.");
        return result;
    }
}


 

2、定義一個 DynamicTest類,執行相關的測試操作

/**
* description: JDK 動態代理Test
* @version v1.0
* @author w
* @date 2018年9月3日下午10:09:02
*/
public class DynamicTest {
    @Test
    public void test(){
        User user = new User("萬年小明");
        FliggyDynamic dynamic = new FliggyDynamic(user);
        Tickets proxy = (Tickets)Proxy.newProxyInstance(user.getClass().getClassLoader(),
        user.getClass().getInterfaces(), dynamic);
        proxy.buyTickets();
    }
}

3、執行結果:

充錢,享受1000M專線搶票
萬年小明 開始搶票啦~
搶票成功. 廣告:沒有什麼是充錢解決不的問題.

 

4、定義 FliggyDynamicRefactor<T>類,實現 InvocationHandler 介面,增加泛型。 (可選)

            重構獲取代理物件方法: Proxy.newProxyInstance .... 避免每次都進行強制型別轉換。

/**
* description: JDK動態代理 。 重構 Proxy.newProxyInstance , 避免每次都進行強制型別轉換。
* @version v1.0
* @author w
* @date 2018年9月3日下午10:52:07
*/
public class FliggyDynamicRefactor<T> implements InvocationHandler{

    /**
    * 目標代理物件 target
    */
    private T target ;

    public FliggyDynamicRefactor(T target) {
        this.target = target;
    }

    /**
    * description: 獲取代理物件
    * @return
    * T
    * @version v1.0
    * @author w
    * @date 2018年9月3日 下午10:55:59
    */
    @SuppressWarnings("unchecked")
    public T getProxy(){
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),
                                          target.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("充錢,享受1000M專線搶票 ");
        Object result = method.invoke(target, args);
        System.out.println("搶票成功. 廣告:沒有什麼是充錢解決不的問題.");
        return result;
    }
}

5、定義 DynamicRefactorTest 類,對重構後的程式碼進行測試 (可選)

/**
* description: JDK 動態代理 重構後的 FliggyDynamicRefactor Test
* @version v1.0
* @author w
* @date 2018年9月3日下午10:09:02
*/
public class DynamicRefactorTest {

    @Test
    public void test(){
        FliggyDynamicRefactor<Tickets> refactor = new FliggyDynamicRefactor<Tickets>(new User("李雷"));
        refactor.getProxy().buyTickets();
    }
}

 

6、執行結果:

充錢,享受1000M專線搶票
李雷 開始搶票啦~
搶票成功. 廣告:沒有什麼是充錢解決不的問題.

7、使用JDK提供的動態代理,可以解決靜態代理中要寫 n多個 xxProxy類的問題。但是可以發現,JDK動態代理只能針對介面的代理。 若一個類,沒有實現任何介面,那麼JDK的動態代理也就無用武之地了。 有解決辦法嗎?有,繼續看 CGLIB的動態代理 ---- 針對類的代理。

 

四、CGLIB動態代理

1、 依賴jar包: cglib-3.2.6.jar

2、定義一個 Car 類,不實現任何 介面的普普通通類

/**
* description: 定義一個普通的 Car類
* @version v1.0
* @author w
* @date 2018年9月3日下午11:27:09
*/
public class Car {
    public void runing(){
        System.out.println("老司機開車啦~ ");
    }
}

 

3、定義一個 CglibProxy類,實現 net.sf.cglib.proxy.MethodInterceptor 介面


import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
* description: 使用 CGLIB 實現動態代理
* @version v1.0
* @author w
* @date 2018年9月3日下午11:12:55
*/
public class CglibProxy implements MethodInterceptor{
    @SuppressWarnings("unchecked")
    public <T> T getProxy(Class<T> clzz){
        return (T) Enhancer.create(clzz, this);
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("前方注意~老司機來了,請速讓道。。 ");
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("老司機已通過前方 over ..");
        return result;
    }
}

4、定義 CglibProxyTest類,針對CglibProxy的測試

/**
* description: 針對 CglibProxy 的測試
* @version v1.0
* @author w
* @date 2018年9月3日下午11:46:34
*/
public class CglibProxyTest {
    @Test
    public void test(){
        CglibProxy proxy = new CglibProxy();
        Car car = proxy.getProxy(Car.class);
        car.runing();
    }
}

5、執行結果:

前方注意~老司機來了,請速讓道。。
老司機開車啦~
老司機已通過前方 over ..

6、可以看到,使用 CGLIB解決了JDK的動態代理只能針對介面代理的問題。 普通的java類都可以進行代理。

 

五、總結

1、代理模式,實現程式碼功能增強,且不影響原始碼,解耦合。

2、靜態代理,針對每一個代理物件都需要都需要一個代理類。

3、JDK動態代理,只能針對介面代理,若目標代理類未實現介面,則無法代理。

4、CGLIB動態代理,可以對普通類進行代理,但目標代理類需提供無參構造方法。

 

 

參考資料: http://www.importnew.com/26116.html

         深入理解CGLIB動態代理機制