1. 程式人生 > 其它 >【Spring Framework】10、代理模式

【Spring Framework】10、代理模式

為什麼要學習代理模式?因為這就是Spring AOP 的底層!【Spring AOP 和 Spring MVC】

代理模式的分類:

  • 靜態代理
  • 動態代理

1、靜態代理

角色分析:

  • 抽象角色:一般會使用介面或抽象類來解決
  • 真實角色:被代理角色
  • 代理角色:代理真實角色,代理真實角色後,我們一般會做一些附屬操作

測試程式碼

1、介面

package com.xg.demo01;

// 租房
public interface Rent {
    public void rent();
}

2、真實角色

package com.xg.demo01;

// 房東
public class Host implements Rent {

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

3、代理角色

package com.xg.demo01;

public class Proxy implements Rent {
    private Host host;
    public Proxy() {
    }
    public Proxy(Host host) {
        this.host = host;
    }
    @Override
    public void rent() {
        host.rent();
        seeHost();
        contract();
        fare();
    }
    // 看房
    public void seeHost() {
        System.out.println("中介帶你看房。");
    }
    // 籤合同
    public void contract() {
        System.out.println("籤租賃合同");
    }
    // 收中介費
    public void fare() {
        System.out.println("中介,收中介費。");
    }
}

4、客戶端訪問代理

package com.xg.demo01;

public class Client {
    public static void main(String[] args) {
        // 房東有房子出租
        Host host = new Host();

        // 代理,中介幫房東出租房子,但是代理角色一般會有一些附屬操作!
        Proxy proxy = new Proxy(host);

        // 不用面對房東,直接找中介即可
        proxy.rent();
    }
}

代理模式的好處

  • 可以使真實角色的操作更加純粹,不用去關注一些公共的業務
  • 公共業務交給了代理!實現了業務的分工!
  • 公共業務發生擴充套件的時候,方便集中管理!

缺點:

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

為什麼要新增代理類

  • 改變原有的業務程式碼在公司中是大忌
  • 原則:儘量不要改變原有的程式碼

加深理解

1、介面

package com.xg.demo02;

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

2、真實角色

package com.xg.demo02;

// 真實物件
public class UserServiceImpl implements UserService{
    @Override
    public void add() {
        System.out.println("增加一個使用者");
    }
    @Override
    public void delete() {
        System.out.println("刪除一個使用者");
    }
    @Override
    public void update() {
        System.out.println("更新一個使用者");
    }
    @Override
    public void query() {
        System.out.println("查詢一個使用者");
    }
}

3、代理角色

package com.xg.demo02;

public class UserServiceProxy implements UserService {
    private UserServiceImpl userService;
    public UserServiceProxy() {
    }
    public void setUserService(UserServiceImpl userService) {
        this.userService = userService;
    }
    @Override
    public void add() {
        log("add");
        userService.add();
    }
    @Override
    public void delete() {
        log("delete");
        userService.delete();
    }
    @Override
    public void update() {
        log("update");
        userService.update();
    }
    @Override
    public void query() {
        log("query");
        userService.query();
    }
    // 列印日誌 方式
    public void log(String msg) {
        System.out.println("[DEBUG]使用了" + msg + "方法");
    }
}

4、客戶端訪問代理

package com.xg.demo02;

public class Client {
    public static void main(String[] args) {
        UserServiceImpl userService = new UserServiceImpl();
        UserServiceProxy userServiceProxy = new UserServiceProxy();
        userServiceProxy.setUserService(userService);

        userServiceProxy.add();
    }
}

2、動態代理

  • 動態代理和靜態代理角色一樣
  • 動態代理的代理類是動態生成的,不是我們直接寫好的!
  • 動態代理分為兩大類:基於介面的動態代理,基於類的動態代理
    • 基於介面:JDK動態代理【使用】
    • 基於類:cglib
    • Java位元組碼:javasist

需要了解兩個類:Proxy:代理,InvocationHandler:呼叫處理程式

  • InvocationHandler是由代理例項的呼叫處理程式實現的介面

    每個代理例項都有一個關聯的呼叫處理程式。 當在代理例項上呼叫方法時,方法呼叫將被編碼並分派到其呼叫處理程式的invoke方法。

  • Proxy提供了建立動態代理類和例項的靜態方法,它也是由這些方法建立的所有動態代理類的超類。

測試程式碼

1、介面

package com.xg.demo03;

// 租房
public interface Rent {
    public void rent();
}

2、真實角色

package com.xg.demo03;

// 房東
public class Host implements Rent {
    @Override
    public void rent() {
        System.out.println("房東要出租房子");
    }
}

3、InvocationHandler 自動生成代理

package com.xg.demo03;

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;
    }
    // 生成代理物件
    public Object getProxy() {
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(), this);
    }
    // 處理代理例項並返回結果
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object invoke = method.invoke(rent, args);
        seeHost();
        contract();
        fare();
        return invoke;
    }
    public void seeHost() {
        System.out.println("中介帶你看房。");
    }
    public void contract() {
        System.out.println("籤租賃合同");
    }
    public void fare() {
        System.out.println("中介,收中介費。");
    }
}

4、代理物件

package com.xg.demo03;

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

        // 代理角色:暫時沒有
        ProxyInvocationHandler pih = new ProxyInvocationHandler();

        // 通過呼叫程式處理角色來處理要呼叫的介面物件!
        pih.setRent(host); // 這裡的proxy就是動態生成的,並沒有寫
        Rent proxy = (Rent) pih.getProxy();
        proxy.rent();
    }
}

加深理解

代理 UserService

InvocationHandler 自動生成代理

package com.xg.demo04;

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;
    }
    // 生成代理物件
    public Object getProxy() {
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }
    // 處理代理例項並返回結果
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log(method.getName());
        Object invoke = method.invoke(target, args);
        return invoke;
    }
    public void log(String msg) {
        System.out.println("[DEBUG]執行了" + msg + "方法");
    }
}

``

Client

package com.xg.demo04;

import com.xg.demo02.UserService;
import com.xg.demo02.UserServiceImpl;

public class Client {
    public static void main(String[] args) {
        UserServiceImpl userService = new UserServiceImpl();
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        pih.setTarget(userService);

        UserService proxy = (UserService) pih.getProxy();
        proxy.add();
    }
}

動態代理的好處

  • 可以使真實角色的操作更加純粹,不用去關注一些公共的業務
  • 公共業務交給了代理!實現了業務的分工!
  • 公共業務發生擴充套件的時候,方便集中管理!
  • 一個動態代理類代理的是一個介面,一般是對應一類業務
  • 一個動態代理可以代理多個類,只要實現了同一個介面