1. 程式人生 > >Java-動態代理模式

Java-動態代理模式

動態代理模式

代理模式為另一個物件提供一個替身或佔位符以控制對這個物件的訪問。—《 Head First設計模式》
換句話說,即客戶不直接操控原物件,而是通過代理物件間接地操控原物件。打個比方,現在房產中介很火,找房子都會經過中介,但是,我們買或者租的房子並不是中介賣或租自己的房子,而是他們的客戶,真正需要租或者賣的是他們的客戶。
代理模式,主要有三種角色:

  1. 抽象角色:通過介面或抽象類宣告真實角色實現的業務方法;
  2. 代理角色:實現抽象角色,是真實角色的代理,通過真實角色的業務邏輯方法來實現抽象方法,並可以附加自己的操作;
  3. 真實角色:實現抽象角色,定義真實角色所要實現的業務邏輯,供代理角色呼叫。

靜態代理

  • UML圖

這裡寫圖片描述

  • 程式碼示例
package com.myapp.pattern.proxypattern.staticpattern;

/**
 * Created by lionel on 16/11/24.
 */
public interface Subject {
    void request();
}
package com.myapp.pattern.proxypattern.staticpattern;

/**
 * Created by lionel on 16/11/24.
 */
public class RealSubject implements
Subject {
public void request() { System.out.println("this is static pattern..."); } }
package com.myapp.pattern.proxypattern.staticpattern;

/**
 * Created by lionel on 16/11/24.
 */
public class ProxySubject implements Subject {
    private RealSubject realSubject;

    public ProxySubject
(RealSubject realSubject) { this.realSubject = realSubject; } public void request() { System.out.println("preparation ..."); realSubject.request(); System.out.println("end ..."); } }
package com.myapp.pattern.proxypattern.staticpattern;

/**
 * Created by lionel on 16/11/25.
 */
public class Client {
    public void proxy() {
        RealSubject realSubject = new RealSubject();
        Subject subject=new ProxySubject(realSubject);
        subject.request();
    }
}

測試結果:
這裡寫圖片描述

不足之處
這種靜態的代理模式固然在訪問無法訪問的資源,增強現有的介面業務功能方面有很大的優點,但是大量使用這種靜態代理,會使我們系統內的類的規模增大,並且不易維護;並且由於 ProxyProxySubject 和 RealSubject 的功能本質上是相同的,都實現了 Subject 的介面,ProxySubject 只是起到了中介的作用,這種代理在系統中的存在,導致系統結構比較臃腫和鬆散

這就產生了動態代理:在執行狀態中,需要代理的地方,根據 Subject 和RealSubject,動態地建立一個Proxy,用完之後,就會銷燬,這樣就可以避免了 Proxy 角色的class在系統中冗雜的問題了。

jdk動態代理

動態代理的實現可以有 java 中的反射機制來完成,反射機制的知識大家可以去網上檢視相關教程,這裡就不再詳細介紹了。

  • jdk 動態代理是怎麼動態的生成一個代理類的
    這就歸功於 java 中的 Proxy類,它可以動態的生成一個代理類物件,呼叫它的newProxyInstance()方法:
public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)

如程式碼所示,它含有三個引數:

  • loader: 用來生成代理類的物件;
  • interfaces:代理類需要實現的class 物件陣列;
  • h:實現了InvocationHandler介面的物件,InvocationHandler類在 java.lang.reflect包中。

InvocationHandler介面下只有一個invoke()方法:

public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;

該方法同樣也有三個引數:

  • proxy:代理類的例項;
  • method:需要執行的方法;
  • args:方法的引數

程式碼示例

package com.myapp.pattern.proxypattern.dynamicpattern.jdk;

/**
 * Created by lionel on 16/11/23.
 */
public interface TicketService {
    /**
     * 賣票
     */
    void sellTicket();

    /**
     * 退票
     */
    void withdrawTicket();
}
package com.myapp.pattern.proxypattern.dynamicpattern.jdk;

/**
 * Created by lionel on 16/11/23.
 */
public class OnlineBookingService implements TicketService {

    public void sellTicket() {
        System.out.println("sell ticket in internet");
    }

    public void withdrawTicket() {
        System.out.println("withdraw ticket in internet");
    }
}
package com.myapp.pattern.proxypattern.dynamicpattern.jdk;

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

/**
 * Created by lionel on 16/11/23.
 */
public class InvocationHandlerImpl implements InvocationHandler {

    private OnlineBookingService onlineBooking;

    public InvocationHandlerImpl() {
    }

    public InvocationHandlerImpl(OnlineBookingService onlineBooking) {
        super();
        this.onlineBooking = onlineBooking;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("you are going to invoking" + method.getName() + "...");
        method.invoke(onlineBooking, args);
        System.out.println(method.getName() + "invocation has been completed...");
        return null;
    }
}
package com.myapp.pattern.proxypattern.dynamicpattern.jdk;

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

/**
 * Created by lionel on 16/11/23.
 */
public class Client {
    public void proxy() {
        OnlineBookingService onlineBooking = new OnlineBookingService();
        InvocationHandler invocationHandler = new InvocationHandlerImpl(onlineBooking);
        Class cls = onlineBooking.getClass();
        TicketService ticketService = (TicketService) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), invocationHandler);
        ticketService.sellTicket();
        System.out.println();
        ticketService.withdrawTicket();
    }
}
//測試類
package com.myapp.pattern.proxyPattern.jdk;

import com.myapp.pattern.proxypattern.dynamicpattern.jdk.Client;
import org.junit.Test;

/**
 * Created by lionel on 16/11/23.
 */
public class ProxyPatternTest {
    @Test
    public void proxyPatternTest(){
        Client client=new Client();
        client.proxy();
    }
}

測試結果:
這裡寫圖片描述

cglib動態代理

通過以上介紹,瞭解了 jdk 動態代理的原理,jdk代理機制只能代理實現了介面的類,而沒有實現介面的類就不能實現的 jdk 的動態代理,而cglib 就是針對類來實現代理
它的原理是對指定的目標類生成一個子類,並覆蓋其中方法實現增強,但因為採用的是繼承,所以不能對final修飾的類進行代理。

攔截器:實現MethodInterceptor介面的類,在intercept方法中實現對代理目標類的方法攔截。

public Object intercept(Object obj, java.lang.reflect.Method method,                    

                Object[] args,MethodProxy proxy) throws Throwable;

如程式碼所示,它有四個引數:
- obj:目標類的例項;
- method:目標類方法的發射物件;
- args:方法引數;
- proxy:代理類例項;

mavan專案 引入cglib依賴

<dependency>
   <groupId>cglib</groupId>
   <artifactId>cglib</artifactId>
   <version>3.2.4</version>
</dependency>

程式碼示例

package com.myapp.pattern.proxypattern.dynamicpattern.cglib;

/**
 * Created by lionel on 16/11/23.
 */
public class Programmer {
    public void code() {
        System.out.println("I am just a programmer");
    }
}
package com.myapp.pattern.proxypattern.dynamicpattern.cglib;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * Created by lionel on 16/11/23.
 */
public class Leader implements MethodInterceptor {
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("Let me see your code...");
        methodProxy.invokeSuper(o, objects);
        System.out.println("oh,this code is too bad...");
        return null;
    }
}
package com.myapp.pattern.proxypattern.dynamicpattern.cglib;

import net.sf.cglib.proxy.Enhancer;

/**
 * Created by lionel on 16/11/23.
 */
public class Client {
    public void proxy() {
        Programmer programmer = new Programmer();
        Leader leader = new Leader();
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(programmer.getClass());
        enhancer.setCallback(leader);
        Programmer proxy = (Programmer) enhancer.create();
        proxy.code();
    }
}
//測試類
package com.myapp.pattern.proxyPattern.cglib;

import com.myapp.pattern.proxypattern.dynamicpattern.cglib.Client;
import org.junit.Test;

/**
 * Created by lionel on 16/11/23.
 */
public class CglibProxyTest {
    @Test
    public void test(){
        Client client=new Client();
        client.proxy();
    }
}

測試結果:
這裡寫圖片描述