java設計模式6——代理模式

java設計模式6——代理模式

一、代理模式介紹:

1.一、爲何要學習代理模式?由於這就是Spring Aop的底層!(SpringAop 和 SpringMvc)

1.二、代理模式的分類:

  • 靜態代理
  • 動態代理

1.三、代理模式關係圖(以租房子爲例)

二、靜態代理

2.一、角色分析:

  1. 抽象角色:通常會使用接口或者抽象類來解決
  2. 真實角色:被代理的角色
  3. 代理客戶:代理真實角色。代理真實角色後,咱們通常會作一些附屬的操做
  4. 客戶:訪問代理對象的人

2.二、例1(租房子演示)

2.2.一、抽象角色實現(房東抽象類)

package com.xgp.company.結構性模式.代理模式.靜態代理.Demo1;

/**
 * 租房
 */
public interface Rent {

    void rent();
}

2.2.二、真實角色實現(房東的實現類)

package com.xgp.company.結構性模式.代理模式.靜態代理.Demo1;

/**
 * 房東
 */
public class Host implements Rent {

    @Override
    public void rent() {
        System.out.println("房東要出租房子!");
    }
}

2.2.三、不經過代理進行租房

package com.xgp.company.結構性模式.代理模式.靜態代理.Demo1;

public class Client {
    public static void main(String[] args) {
        //直接找房東
        Host host = new Host();
        host.rent();
    }
}

運行結果:

房東要出租房子!

2.2.四、弊端

  1. 在實際的生活場景中,房東可能只賣房,而不負責租房
  2. 若是要提升租房子的成功率,必然還須要一些其餘的服務,如:看房、談價錢、籤合同等,而這些房東並不想參與,所以產生了中介,也就是代理。

2.2.五、代理出現

package com.xgp.company.結構性模式.代理模式.靜態代理.Demo1;

public class Proxy implements Rent {

    private Host host;

    public Proxy() {

    }

    public Proxy(Host host) {
        this.host = host;
    }

    @Override
    public void rent() {
        seeHouse();
        host.rent();
        fare();
        hetong();
    }

    //看房
    public void seeHouse() {
        System.out.println("中介代理看房");
    }

    //收中介費
    public void fare() {
        System.out.println("收中介費");
    }

    public void hetong() {
        System.out.println("籤合同");
    }
}

2.2.六、經過代理來租房子

package com.xgp.company.結構性模式.代理模式.靜態代理.Demo1;

public class Client {
    public static void main(String[] args) {
/*        //直接找房東
        Host host = new Host();
        host.rent();*/

        //經過代理去租房子
        //房東要租房子
        Host host = new Host();
        //代理
        //中介幫房東租房子,可是,中介一半都會有一些附屬操做
        Proxy proxy = new Proxy(host);
        proxy.rent();
    }
}

運行結果:

中介代理看房
房東要出租房子!
收中介費
籤合同

2.三、分析:

代理模式的好處

  1. 可使真實角色的操做更加純粹,不用去關注一些公共的業務。
  2. 公共的業務交給了代理角色去完成,實現了業務的分工。
  3. 公共業務發生擴展的時候,方便集中管理。

缺點:

  • 一個真實角色就會產生一個代理的角色,代碼量翻倍,開發效率變低。

2.四、例1(增刪改查業務的演示)

2.4.一、編寫service的抽象類

package com.xgp.company.結構性模式.代理模式.靜態代理.Demo2;

public interface UserService {
    void add();
    void del();
    void update();
    void query();
}

2.4.二、編寫service的實現類

package com.xgp.company.結構性模式.代理模式.靜態代理.Demo2;

/**
 * 真實對象
 */
public class UserServiceImpl implements UserService {
    @Override
    public void add() {
        System.out.println("增長了一個用戶");
    }

    @Override
    public void del() {
        System.out.println("刪除了一個用戶");
    }

    @Override
    public void update() {
        System.out.println("更新了一個用戶");
    }

    @Override
    public void query() {
        System.out.println("查詢了一個用戶");
    }
}

2.4.三、若是此時想不改變原有的代碼,而增長方法日誌打印的功能,此時代理誕生

package com.xgp.company.結構性模式.代理模式.靜態代理.Demo2;

/**
 * 代理角色,增長日誌的功能
 */
public class UserServiceProxy implements UserService {

    private UserService userService;

    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    @Override
    public void add() {
        userService.add();
        log("add");
    }

    @Override
    public void del() {
        userService.del();
        log("del");
    }

    @Override
    public void update() {
        userService.update();
        log("update");
    }

    @Override
    public void query() {
        userService.query();
        log("query");
    }

    public void log(String msg) {
        System.out.println("使用了" + msg + "方法!");
    }
}

2.4.四、編寫客戶端類,使用service的方法,並打印日誌

package com.xgp.company.結構性模式.代理模式.靜態代理.Demo2;

public class Client {
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();

        UserServiceProxy proxy = new UserServiceProxy();
        proxy.setUserService(userService);

        proxy.add();
        proxy.del();
        proxy.update();
        proxy.query();
    }
}

運行結果:

增長了一個用戶
使用了add方法!
刪除了一個用戶
使用了del方法!
更新了一個用戶
使用了update方法!
查詢了一個用戶
使用了query方法!

2.五、反思:在實際的業務中,橫向開發的好處

能夠保證原有代碼的可行性,不會對軟件原來的版本進行破壞,向過去兼容。

三、動態代理

3.一、動態代理的特色

  1. 動態代理和靜態代理同樣
  2. 動態代理的代理類是動態生成的,不是咱們直接寫的

3.二、動態代理實現方式的分類

  • 基於接口——JDK動態代理(本節須要講述的方式)
  • 基於類:cglib
  • java字節碼實現:javasist

3.三、須要瞭解的兩個類

  • Proxy:代理類
  • InvocationHandler:調用處理程序類

具體的使用參照java的api

3.四、例1——房東租房的例子

3.4.一、房東的接口和實現類不變

3.4.二、實現自動生成代理的類

package com.xgp.company.結構性模式.代理模式.動態代理.Demo1;

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

/**
 * 等下咱們會用這個類,自動生成代理類
 */
public class ProxyInvocationHandler implements InvocationHandler {

    /**
     * 被代理的接口
     */
    private Rent rent;

    public void setRent(Rent rent) {
        this.rent = rent;
    }

    /**
     * 得到一個代理
     * @return
     */
    public Object getProxy() {
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this);
    }

    /**
     * 處理代理實例,並放回結果
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //用反射來執行方法
        seeHouse();     //調用代理自身的方法
        //動態代理的本質,就是使用反射機制來實現
        Object result = method.invoke(rent, args);
        fare();
        hetong();
        return result;
    }

    public void seeHouse() {
        System.out.println("中介帶看房子!");
    }

    //收中介費
    public void fare() {
        System.out.println("收中介費");
    }

    public void hetong() {
        System.out.println("籤合同");
    }
}

3.4.三、實現客戶端使用代理來租房子

package com.xgp.company.結構性模式.代理模式.動態代理.Demo1;

public class Client {
    public static void main(String[] args) {
        //真實角色
        Host host = new Host();

        //代理角色
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        //經過調用程序處理角色,來處理咱們要調用的接口對象
        pih.setRent(host);

        //放回代理類
        //這裏的代理類就是動態生成的,咱們並無寫他
        Rent proxy = (Rent) pih.getProxy();
        proxy.rent();
    }
}

運行結果:

中介帶看房子!
房東要出租房子!
收中介費
籤合同

3.五、例2——增刪改查的例子

3.5.一、先來實現一個萬能的代理生成類

package com.xgp.company.結構性模式.代理模式.動態代理.Demo2;

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

/**
 * 萬能代理類
 */
public class ProxyInvocationHandler implements InvocationHandler {

    /**
     * 被代理的接口
     */
    private Object target;

    public void setTarget(Object target) {
        this.target = target;
    }

    /**
     * 得到一個代理
     * @return
     */
    public Object getProxy() {
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }

    /**
     * 處理代理實例,並放回結果
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //用反射來執行方法
        //動態代理的本質,就是使用反射機制來實現
        Object result = method.invoke(target, args);
        log(method.getName());
        return result;
    }

    public void log(String msg) {
        System.out.println("使用了" + msg + "方法!");
    }
}

也就是將上一個例子的具體實現類——房東類,替換成Object類

3.5.二、UserService的接口和實現類不變

3.5.三、編寫客戶端類使用代理調用增刪改查方法

package com.xgp.company.結構性模式.代理模式.動態代理.Demo2;

public class Client {
    public static void main(String[] args) {
        //真實角色
        UserService userService = new UserServiceImpl();
        //代理角色
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        pih.setTarget(userService);
        UserService proxy = (UserService) pih.getProxy();

        proxy.add();
        proxy.del();
        proxy.update();
        proxy.query();
    }
}

運行結果:

增長了一個用戶
使用了add方法!
刪除了一個用戶
使用了del方法!
更新了一個用戶
使用了update方法!
查詢了一個用戶
使用了query方法!

3.六、弊端分析:

雖然使用動態代理可以節省代碼量,而且實現靜態代理的所有優勢。可是,動態代理的核心是反射技術,經過反射技術調用方法效率較大,所以也可能影響系統效率。

3.七、動態代理的好處

一個動態代理的是一個接口,通常就是對應的一類業務。

相關文章
相關標籤/搜索