Java代理之靜態代理

Mr.棟發表於2020-09-07

什麼是代理

代理就是給目標物件一個代理物件,並由代理物件控制目標的引用。

為什麼要使用代理模式

1、通過引入代理物件的方式,可以間接的訪問目標物件,避免直接訪問目標物件給系統帶來不必要的複雜性。
2、通過代理物件可以對原有的業務進行業務增強處理。

舉例:如果我們需要買國外的某一件商品A,這個時候我們一般有兩個途徑要麼直接去國外買,要麼可以找一些代購人員幫我們去購買。在這種情況下,我們由於直接去國外買,實在是太耗軟妹幣,而且還要花時間等等,這個時候我們最優的選擇就是找代購購買,這樣也幫我們省去了很多麻煩的事情。

代理模式類圖

程式碼示例

抽象物件:

public interface ITargetFactoryService {

    void sale(String name);
}

目標物件:

@Slf4j
public class TargetFactoryServiceImpl implements ITargetFactoryService {

    @Override
    public void sale(String name) {
        log.info(name+"購買了商品A");
    }
}

代理物件:

@Slf4j
public class ProxyImpl implements ITargetFactoryService {

    public ITargetFactoryService service;

    public ProxyImpl(ITargetFactoryService service){
        super();
        this.service = service;
    }

    @Override
    public void sale(String name) {
        before();
        service.sale("代購");
        after();

    }

    /**
     * 後置增強
     */
    private void after() {
        log.info("代購在購買後得到了市場調查結果");
    }

    /**
     * 前置增強
     */
    private void before() {
        log.info("代購在購買前做了市場調查");
    }
}

測試類:

@Slf4j
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, JdbcTemplateAutoConfiguration.class})
public class SpsringJdbcApplication {

  public static void main(String[] args) {
    TargetFactoryServiceImpl service = new TargetFactoryServiceImpl();
    ProxyImpl proxy = new ProxyImpl(service);
    proxy.sale("代購");
    SpringApplication.run(SpsringJdbcApplication.class, args);
  }

}

測試結果:

我們可以在程式碼示例中清晰的看到,在代理類中,代理物件包含了目標物件,並且在業務處理上進行了一定的業務擴充套件,但是卻和目標物件繼承於同一個介面。但是此擴充套件基於Spring AOP來講,以更加專業的叫法為前置增強、後置增強。

此類代理便是我們常說的靜態代理,靜態代理適合在業務比較簡單,實現類少,需求變化不頻繁,但是卻要對原有目標服務物件功能進行擴充套件,並且不去修改原有服務,這個時候我們就可以選擇使用靜態代理。

靜態代理的缺點

如果此時我們業務需要進行擴充套件,我們的代購同學在經過市場調查以後,發現商品B更加受大家歡迎,這個時候我們就需要對自己的業務進行擴充套件了,怎麼擴充套件呢?一起接著往下看。

抽象物件:

public interface ITargetFactoryBService {

    void saleB(String name);
}

目標物件:


@Slf4j
public class ITargetFactoryBServiceImpl implements ITargetFactoryBService {

    @Override
    public void saleB(String name) {
        log.info(name + "購買了商品B");
    }
}

代理物件:

@Slf4j
public class ProxyTwoImpl implements ITargetFactoryService, ITargetFactoryBService {

    public ITargetFactoryService service;

    public ITargetFactoryBService bService;

    public ProxyTwoImpl(ITargetFactoryService service,ITargetFactoryBService bService){
        super();
        this.service = service;
        this.bService = bService;
    }

    @Override
    public void sale(String name) {
        before();
        service.sale("代購");
        after();

    }

    @Override
    public void saleB(String name) {
        before();
        bService.saleB("代購");
        after();
    }

    /**
     * 後置增強
     */
    private void after() {
        log.info("代購在購買後得到了市場調查結果");
    }

    /**
     * 前置增強
     */
    private void before() {
        log.info("代購在購買前做了市場調查");
    }


}

測試類:


@Slf4j
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, JdbcTemplateAutoConfiguration.class})
public class SpsringJdbcApplication {

  public static void main(String[] args) {
    TargetFactoryServiceImpl service = new TargetFactoryServiceImpl();
    ITargetFactoryBServiceImpl bService = new ITargetFactoryBServiceImpl();
    ProxyTwoImpl proxy2 = new ProxyTwoImpl(service, bService);
    proxy2.sale("代購");
    proxy2.saleB("代購");
    SpringApplication.run(SpsringJdbcApplication.class, args);
  }

}

結果:

我們可以看到,在實現業務擴充套件的時候,需要對原有的代理類進行修改,如果後期我們需要擴充套件的業務較多的時候,這個類將變的更加繁雜,大量的繼承以及方法重寫,以至於牽一髮而動全身,所以在這種業務擴充套件性高、業務變化頻繁的情況下我們不建議使用靜態代理。

靜態代理總結:

1、違反Java設計模式開閉原則,即:程式對外擴充套件開放,對修改關閉。當需求進行變更時,我們應該是新增程式碼塊來實現,而不是在原來的程式碼中進行修改實現。
2、擴充套件性很差。
3、可維護性差。
4、程式碼耦合度高。

如果覺著不錯可以關注公眾號:Java禿頭猿,專注於專案實際開發技術分享。

相關文章