設計模式(一)快來了解一下無處不在的代理模式

一、什麼是代理模式

先來看看百度的解釋

代理模式的定義:爲其他對象提供一種代理以控制對這個對象的訪問。在某些情況下,一個對象不適合或者不能直接引用另一個對象,而代理對象可以在客戶端和目標對象之間起到中介的作用。
——百度百科

個人感覺這個將的太抽象了,不過這裏有個概念-“中介”,這個詞就很能體現代理模式的精髓,一切代理模式其實都是一箇中介,能夠讓使用者能夠更加方便去使用,而不需要考慮其中複雜的過程。

二、使用代理模式的優缺點

1.優點

  • 讓代碼的職責更加清晰,經過代理之後,在使用方法時就不需要考慮業務邏輯之外的東西
  • 具有高擴展性,能夠很好的與其他方法進行組合
  • 代理對象可以在客戶端和目標對象之間起到中介的作用,這樣起到了中介的作用和保護了目標對象的作用

2.缺點

  • 增加了系統複雜度,這也是不可避免的,代理模式在不影響使用的情況下,增加了額外的功能,必然會增加複雜度
  • 由於在客戶端和真實對象之間增加了代理對象,因此有些類型的代理模式可能會造成請求的處理速度變慢(個人覺得,實例之間的相互調用應該是一個很迅速的過程,有沒有代理的效率應該是差不多的)

三、代理模式的分類

我這裏不是按照實現方法來進行分類的,而是按照代理模式的本質來分類的。

1.增強型代理

1. 特點

核心功能不變(不能修改原來的功能)
業務無關性(不修改核心的業務邏輯,降低系統的複雜性)
使用習慣不變
附加新功能(只是讓需要代理的功能附加不影響原來的功能的功能,相當於加個buff)
普適性(增強的功能能夠加到其他方法中)

總的來說,增強型代理只是對一個方法進行一些無關業務的操作,安全校驗,上鎖,記錄等一些無關業務的操作。

2. 常見的例子

增強代理的實現可以有靜態代理,也可以有動態代理
常見的例子有:

  • spring的aop,聲明型事物
  • 監控操作(當用戶操作後,做一個記錄)

2.鏈接型代理

1. 特點

跟增強型代理一些相似的特點
核心功能不變
業務無關性
使用更加方便

總的來說,鏈接型代理就是簡化我們對方法的調用,也是不改變業務,一般來說,不會做一些增強型的操作。

2. 常見的例子

鏈接型代理的實現同樣也是可以靜態,也可以動態。
常見的例子有:

  • MyBtis的mapper代理
  • springCloud中的ribbon,zuul等

四、代理模式的實現

1.靜態代理

靜態代理就是採用手動寫代碼的方式來實現,可以在不修改目標對象功能的前提下,實現功能擴展擴展多樣化,但是需要編寫很多方法的擴展,管理成本增大
首先需要創建一個接口,接口中定義被代理類和代理類都要實現的方法,然後創建被代理類和代理類

/**
 * @author xxj
 * 靜態代理測試
 */
public class StaticProxy {
    public static void main(String[] args) {
        Company company=new Company("房地產公司","靠海別墅");
        CompanyProxy companyProxy=new CompanyProxy(company,"小張");

        companyProxy.sale();
    }

    /**
     * 接口
     * 賣東西的接口
     */
    public interface Salor{
        public void sale();
    }

    /**
     * 被代理類
     * 需要代理的公司
     */
    public static class   Company implements Salor{
        String name;
        String product;
        public Company(String name,String product){
            this.name=name;
            this.product=product;
        }
        @Override
        public void sale() {
            System.out.println(name+"賣出"+product);
        }
    }
    /**
     * 代理類
     * 中介
     */
    public static class CompanyProxy implements Salor{
        private Company company;
        private String worker;
        /**
         * @param company 需要代理的公司
         */
        public CompanyProxy(Company company,String worker){
            this.company=company;
            this.worker=worker;
        }
        @Override
        public void sale() {
            System.out.println(worker+"推銷--聯繫客戶---");
            System.out.println("聯繫"+company.name+"進行交易--");
            company.sale();
            System.out.println(worker+"拿提成--");
        }
    }
}

個人感覺這更像鏈接型代理,因爲CompanyProxy的sale方法中是有做相關操作的(如果把打印到控制檯理解爲一個操作),而增強型代理則是打印一下狀態信息或者是監控,而不會去執行company.sale()

2.動態代理

動態代理生成的代理類都是通過動態拼接字節碼產生的,實際上代理類並不存在,而靜態代理中代理類則是真是存在的

JDK動態代理

JDK動態代理就不需要手動地去擴展方法的功能了,而是由一個實現了InvocationHandler接口的類來統一管理,不用寫每個方法的擴展,使代理類的管理成本降低,但是會是代理類的創建變得麻煩,這就可以套上一個工廠模式來創建代理類了

首先依然是要創建一個接口被代理類需要實現這個接口,然後在創建一箇中介類實現InvocationHandler接口,然後調用Proxy提供的靜態方法newProxyInstance來創建代理類(注意需要用接口接收)

/**
 * @author xxj
 * JDK動態代理
 */
public class DynamicProxy {
    public static void main(String[] args) {
        Company company=new Company("房產公司","傍山別墅");
        //中介公司
        CompanyHandler companyHandler=new CompanyHandler(company,"啥都能賣公司");
        //類加載器,可以強行理解爲銷售組長,要由他來叫人幹活
        ClassLoader classLoader=company.getClass().getClassLoader();
        //叫張三來替啥都能賣公司幹活
        Salor zhangsan= (Salor) Proxy.newProxyInstance(classLoader,
                company.getClass().getInterfaces(),companyHandler);
        zhangsan.sale();
    }

    /**
     * handle類
     * 相當於中介公司
     */
    public static class CompanyHandler implements InvocationHandler{
        private Company company;
        private String agentCompany;
        public CompanyHandler(Company company,String agentCompany){
            this.company=company;
            this.agentCompany=agentCompany;
        }
        /**
         * @param proxy 貌似不能使用
         * @param method 調用invoke的method信息
         * @param args method的參數信息
         * @return
         * @throws Throwable
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println(agentCompany+"推銷--聯繫客戶---");
            System.out.println("聯繫"+company.name+"進行交易--");
            //執行company的方法
            method.invoke(company,args);
            System.out.println(agentCompany+"拿提成--");
            return null;
        }
    }

    /**
     * 被代理類
     * 需要代理的公司
     */
    public static class  Company implements Salor {
        String name;
        String product;
        public Company(String name,String product){
            this.name=name;
            this.product=product;
        }
        @Override
        public void sale() {
            System.out.println(name+"賣出"+product);
        }
    }
    /**
     * 接口
     * 賣東西的接口
     */
    public interface Salor{
        public void sale();
    }
}
Cglib動態代理

Cglib動態代理使用起來就比JDK動態代理方便多了,只需要寫一個ProxyFactory類就可以給其他類做代理了,而且不需要再寫接口了,但是它生成代理類的過程比較複雜,還需要依賴第三方jar包
首先,需要導入spring的spring-corejar包,然後只需要寫一個代理工廠類,需要實現MethodInterceptor接口,還要在內部提供構建代理類的方法,直接把被代理類放入代理工廠類就可以生成代理類

maven

<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>3.2.8.RELEASE</version>
    </dependency>
/**
 * @author xxj
 * Cglib動態代理
 */
public class CglibDynamicProxy {

    public static void main(String[] args) {
        Company company=new Company("房產公司","私人莊園");
        Company proxy= (Company) new ProxyFactory(company,"一定能賣出公司").getProxyInstance();
        proxy.sale();
    }
    public static class ProxyFactory implements MethodInterceptor {
        private Company company;
        private String agentCompany;
        public ProxyFactory(){}
        public ProxyFactory(Company company, String agentCompany){
            this.company=company;
            this.agentCompany=agentCompany;
        }

        //給目標對象創建一個代理對象
        public Object getProxyInstance(){
            //1.工具類
            Enhancer en = new Enhancer();
            //2.設置父類
            en.setSuperclass(company.getClass());
            //3.設置回調函數
            en.setCallback(this);
            //4.創建子類(代理對象)
            return en.create();
        }
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println(agentCompany+"推銷--聯繫客戶---");
            System.out.println("聯繫"+company.name+"進行交易--");
            //執行company的方法
            method.invoke(company,objects);
            System.out.println(agentCompany+"拿提成--");
            return null;
        }
    }

    /**
     * 被代理類
     * 需要代理的公司
     */
    public static class   Company  {
        String name;
        String product;
        public Company(){}
        public Company(String name,String product){
            this.name=name;
            this.product=product;
        }
        public void sale() {
            System.out.println(name+"賣出"+product);
        }
    }
}

五、總結

無論那種代理模式都需要滿足不改變核心功能不改變業務邏輯這兩個特點,一旦違背了,就會極大的增加系統的複雜度。

這裏分別舉兩個生活中的例子來作爲本章節的結束吧。
增強型代理,就像是微波爐(代理對象),當我們需要加熱食物(被代理對象)時,就將食物放入微波爐中,然後我們就會得到一個加熱了的食物(附加新功能)。這個過程沒有改變食物的本質(不改變核心功能),我們依然能喫(說加熱太燙吃不了的,你就槓吧)(使用習慣不變)一切食物都可以加熱(普適性)你想加熱別的東西也可以。
鏈接型代理,這個可以舉的例子太多了,像宅急送,我們只需要手機下單就可以購買漢堡(使用更加方便)我們手機下單最終做的事情就是買漢堡(不改變核心功能

——————————————————————————————
如果本文章內容有問題,請直接評論或者私信我。如果覺得寫的還不錯的話,點個贊也是對我的支持哦
未經允許,不得轉載!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章