Java代理模式詳解

代理是一種經常使用的設計模式,其目的就是爲其餘對象提供一個代理以控制對某個對象的訪問。代理類負責爲委託類預處理消息,過濾消息並轉發消息,以及進行消息被爲拖累執行後的後續處理。

爲了保持行爲的一致性,代理類和委託類一般會實現相同的接口,因此在訪問者看來二者沒有絲毫的區別。經過代理類這中間一層,能有效控制對委託類對象的直接訪問,也能夠很好地隱藏和保護委託類對象,同時也爲實施不一樣控制策略預留了空間,從而在設計上得到了更大的靈活性。java

更通俗的說,代理解決的問題當兩個類須要通訊時,引入第三方代理類,將兩個類的關係解耦,讓咱們只瞭解代理類便可,並且代理的出現還可讓咱們完成與另外一個類之間的關係的統一管理,可是切記,代理類和委託類要實現相同的接口,由於代理真正調用的仍是委託類的方法。程序員

按照代理的建立時期,代理類能夠分爲兩種:
靜態:由程序員建立代理類或特定工具自動生成源代碼再對其編譯。在程序運行前代理類的.class文件就已經存在了。
動態:在程序運行時運用反射機制動態建立而成設計模式

下面是兩個例子說明了靜態代理和動態代理的使用方法:數組

靜態代理模式

1
2
3
4
5
6
7
8
9
10
11
12
13
package Proxy;

/**
 * Created by benjamin on 1/15/16.
 */
public interface UserManager {

    void addUser(String userId, String userName);
    void deleteUser(String userId);
    String findUser(String userId);
    void modifyUser(String userId, String userName);

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package Proxy;

/**
 * Created by benjamin on 1/15/16.
 */
public class UserManagerImpl implements UserManager {

    public void addUser(String userId, String userName) {
        System.out.println("UserManagerImpl.addUser: " + userId + ": " + userName);
    }

    public void deleteUser(String userId) {
        System.out.println("UserManagerImpl.deleteUser");
    }

    public String findUser(String userId) {
        System.out.println("UserManagerImpl.findUser");
        return "benjamin";
    }

    public void modifyUser(String userId, String userName) {
        System.out.println("UserManagerImpl.modifyUser");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package Proxy;

/**
 * Created by benjamin on 1/15/16.
 */
public class UserManagerProxy implements UserManager {

    private UserManager object;

    // 構造函數傳入目標對象
    public UserManagerProxy(UserManager object) {
        this.object = object;
    }

    public void addUser(String userId, String userName) {
        try {

            // 添加基本的日誌打印功能
            System.out.println("開始addUser");
            object.addUser(userId, userName);
            System.out.println("成功addUser");

        } catch (Exception e) {
            System.err.println("error addUser");
        }
    }

    public void deleteUser(String userId) {
        object.deleteUser(userId);
    }

    public String findUser(String userId) {
        return object.findUser(userId);
    }

    public void modifyUser(String userId, String userName) {
        object.modifyUser(userId, userName);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package Proxy;

/**
 * Created by benjamin on 1/15/16.
 */
public class Client {

    public static void main(String[] args) {
        UserManager userManager = new UserManagerProxy(new UserManagerImpl());
        userManager.addUser("1111", "張三");

        /**
         * 開始addUser
           UserManagerImpl.addUser: 1111: 張三
           成功addUser
         */
    }
}

相信有一點基礎的同窗均可以看出靜態代理的缺點:
1)代理類和委託類實現了相同的接口,代理類經過委託類實現了相同的方法。這樣就出現了大量的代碼重複。若是接口增長一個方法,除了全部實現類須要實現這個方法外,全部代理類也須要實現此方法。增長了代碼維護的複雜度。
2)代理對象只服務於一種類型的對象,若是要服務多類型的對象。勢必要爲每一種對象都進行代理,靜態代理在程序規模稍大時就沒法勝任了。如上的代碼只是爲UserManager類的訪問提供了代理,可是若是還要爲其餘類如Department類提供代理的話,就須要咱們再次添加代理Department的代理類。緩存

這樣咱們必需要引入動態代理:
在上面的示例中,一個代理只能代理一種類型,並且是在編譯器就已經肯定被代理的對象。而動態代理是在運行時,經過反射機制實現動態代理,而且可以代理各類類型的對象安全

動態代理模式

原來的接口和實現類都不變,這裏就再也不重複寫了,只是代理類使用了動態代理來實現。服務器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package Proxy;

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

/**
 * Created by benjamin on 1/15/16.
 */
public class UserManagerProxy implements InvocationHandler {

    // 目標對象
    private Object targetObject;

    public Object newProxyInstance(Object targetObject) {
        this.targetObject = targetObject;

        // 第一個參數指定產生代理對象的類加載器,須要將其指定爲和目標對象同一個類加載器
        // 第二個參數要實現和目標對象同樣的接口,因此只須要拿到目標對象的實現接口
        // 第三個參數代表這些被攔截的方法在被攔截時須要執行哪一個InvocationHandler的invoke方法
        // 根據傳入的目標返回一個代理對象
        return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);
    }

    /**
     * 關聯的這個實現類的方法被調用時將被執行
     * @param proxy 代理
     * @param method    原對象被調用的方法
     * @param args  方法的參數
     * @return  原方法的返回值
     * @throws Throwable
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object ret = null;

        try {

            System.out.println("start invoke-->");

            // 調用目標方法
            ret = method.invoke(targetObject, args);

            System.out.println("success invoke-->");

        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("error-->");
            throw e;
        }
        return ret;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package Proxy;

/**
 * Created by benjamin on 1/15/16.
 */
public class Client {

    public static void main(String[] args) {
        UserManager userManager = (UserManager)new UserManagerProxy().newProxyInstance(new UserManagerImpl());
        userManager.addUser("1111", "張三");

        /**
         * start invoke-->
           UserManagerImpl.addUser: 1111: 張三
           success invoke-->
         */
    }
}

AOP模式分析

AOP(AspectOrientedProgramming):將日誌記錄,性能統計,安全控制,事務處理,異常處理等代碼從業務邏輯代碼中劃分出來,經過對這些行爲的分離,咱們但願能夠將它們獨立到非指導業務邏輯的方法中,進而改變這些行爲的時候不影響業務邏輯的代碼—解耦。
正是由於在全部的類裏,核心代碼以前的操做和核心代碼以後的操做都作的是一樣的邏輯,所以咱們須要將它們提取出來,單獨分析,設計和編碼,這就是咱們的AOP思想。一句話說,AOP只是在對OOP的基礎上進行進一步抽象,使咱們的類的職責更加單一。app

動態代理的優勢:
動態代理與靜態代理相比較,最大的好處是接口中聲明的全部方法都被轉移到調用處理器一個集中的方法中處理(InvocationHandler.invoke)。這樣,在接口方法數量比較多的時候,咱們能夠進行靈活處理,而不須要像靜態代理那樣每個方法進行中轉。並且動態代理的應用使咱們的類職責更加單一,複用性更強框架

cglib動態代理實現


JDK自從1.3版本開始,就引入了動態代理,JDK的動態代理用起來很是簡單,可是它有一個限制,就是使用動態代理的對象必須實現一個或多個接口 。若是想代理沒有實現接口的類可使用CGLIB包。
CGLIB是一個強大的高性能的代碼生成包。它被許多AOP的框架(例如Spring AOP)使用,爲他們提供方法的interception(攔截)。Hibernate也使用CGLIB來代理單端single-ended(多對一和一對一)關聯。EasyMock經過使用模仿(moke)對象來測試java代碼的包。它們都經過使用CGLIB來爲那些沒有接口的類建立模仿(moke)對象。
  CGLIB包的底層是經過使用一個小而快的字節碼處理框架ASM,來轉換字節碼並生成新的類。不鼓勵直接使用ASM,由於它要求你必須對JVM內部結構包括class文件的格式和指令集都很熟悉。
下面經過一個實例來說解cglib:
 socket

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.benjamin;

/**
 * 定義一個HelloWorld類
 * 
 * @author benjamin 
 * 
 */
public class HelloWorld {
    public void sayHelloWorld() {
        System.out.println("HelloWorld!");
    }
}


 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package com.benjamin;

import java.lang.reflect.Method;

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

/**
 * 經過Cglib實如今方法調用先後向控制檯輸出兩句字符串
 * 
 * @author benjamin
 *
 */
public class CglibProxy implements MethodInterceptor {
    //要代理的原始對象
    private Object obj;
    
    public Object createProxy(Object target) {
        this.obj = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.obj.getClass());// 設置代理目標
        enhancer.setCallback(this);// 設置回調
        enhancer.setClassLoader(target.getClass().getClassLoader());
        return enhancer.create();
    }


    /**
     * 在代理實例上處理方法調用並返回結果
     * 
     * @param proxy
     *            代理類
     * @param method
     *            被代理的方法
     * @param params
     *            該方法的參數數組
     * @param methodProxy
     */
    public Object intercept(Object proxy, Method method, Object[] params,
            MethodProxy methodProxy) throws Throwable {
        Object result = null;
        // 調用以前
        doBefore();
        // 調用原始對象的方法
        result = methodProxy.invokeSuper(proxy, params);
        // 調用以後
        doAfter();
        return result;
    }

    private void doBefore() {
        System.out.println("before method invoke");
    }

    private void doAfter() {
        System.out.println("after method invoke");
    }

}


 

1
2
3
4
5
6
7
8
9
10
11
package com.benjamin;

public class HelloWorldTest {

    public static void main(String[] args) {
        HelloWorld helloWorld=new HelloWorld();
        CglibProxy cglibProxy=new CglibProxy();
        HelloWorld hw=(HelloWorld)cglibProxy.createProxy(helloWorld);
        hw.sayHelloWorld();
    }
}



運行結果爲:
 

1
2
3
before method invoke
HelloWorld!
after method invoke


 

遠程代理的實現

遠程代理分爲服務端和客戶端,都必須存在接口定義的文件。這樣客戶端只須要經過遠程代理就能夠擁有存在服務端方法同樣的感受。

共同的代碼爲:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
package remoteProxy;

import java.io.Serializable;

/**
 * Created by piqiu on 1/15/16.
 */
public class Call implements Serializable {

    private static final long serialVersionUID = 4924678505695074146L;

    private String className;
    private String methodName;
    private Class[] paramTypes;
    private Object[] params;
    private Object result;

    public Call() {}

    public Call(String className, String methodName, Class[] paramTypes, Object[] params) {
        this.className = className;
        this.methodName = methodName;
        this.paramTypes = paramTypes;
        this.params = params;
    }

    public String toString() {
        StringBuffer sb = new StringBuffer();
        sb.append("{className=" + className).append(",methodName=" + methodName).append(",result=" + result).append("}");
        return sb.toString();
    }

    public String getClassName() {
        return className;
    }

    public void setClassName(String className) {
        this.className = className;
    }

    public String getMethodName() {
        return methodName;
    }

    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }

    public Class[] getParamTypes() {
        return paramTypes;
    }

    public void setParamTypes(Class[] paramTypes) {
        this.paramTypes = paramTypes;
    }

    public Object[] getParams() {
        return params;
    }

    public void setParams(Object[] params) {
        this.params = params;
    }

    public Object getResult() {
        return result;
    }

    public void setResult(Object result) {
        this.result = result;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
package remoteProxy;

import java.rmi.RemoteException;
import java.util.Date;

/**
 * Created by benjamin on 1/15/16.
 */
public interface IHelloService {

    String echo(String msg) throws RemoteException;
    Date getTime() throws RemoteException;
}

服務端代碼爲:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package remoteProxy.Server;

import remoteProxy.IHelloService;

import java.util.Date;

/**
 * Created by benjamin on 1/15/16.
 */
public class HelloServiceImpl implements IHelloService {

    public String echo(String msg) {
        System.out.println("HelloServiceImpl.echo: " + msg);
        return msg;
    }

    public Date getTime() {
        System.out.println("HelloServiceImpl.getTime: " + new Date());
        return new Date();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
package remoteProxy.Server;

import remoteProxy.Call;

import java.io.*;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by benjamin on 1/15/16.
 */
public class SimpleServer {
    private Map remoteObjects = new HashMap();

    /** 把一個遠程對象放到緩存中 **/
    public void register(String className, Object remoteObject) {
        remoteObjects.put(className, remoteObject);
    }

    public void service() throws Exception {
        ObjectInputStream ois = null;
        ObjectOutputStream oos = null;
        Socket socket = null;
        try {
            ServerSocket serverSocket = new ServerSocket(8888);
            System.out.println("服務器啓動......");
            while (true) {
                socket = serverSocket.accept();

                ois = new ObjectInputStream(socket.getInputStream());  // 讀取對象
                oos = new ObjectOutputStream(socket.getOutputStream());    // 獲得輸出流

                Call call = (Call)ois.readObject(); // 接收客戶端發送的Call對象

                call = invoke(call);    // 執行完操做拿回結果
                oos.writeObject(call);  // 發送結果

            }
        } finally {
            // 關閉流
            if (ois != null) ois.close();
            if (oos != null) oos.close();
            if (socket != null) socket.close();
        }

    }

    public Call invoke(Call call) {
        Object result = null;
        try {

            String className = call.getClassName();
            String methodName = call.getMethodName();
            Object[] params = call.getParams();
            Class[] paramTypes = call.getParamTypes();
            Class classType = Class.forName(className);

            Method method = classType.getMethod(methodName, paramTypes);
            Object remoteObject = remoteObjects.get(className); // 從緩存中讀取相關的緩存對象

            if (remoteObject == null) {
                throw new Exception(className + "的遠程對象不存在");
            } else {
                result = method.invoke(remoteObject, params);
            }

        } catch (Exception e) {
            result = e;
        }
        call.setResult(result);
        return call;
    }

    public static void main(String[] args) throws Exception {
        SimpleServer server = new SimpleServer();
        server.register("remoteProxy.IHelloService", new HelloServiceImpl());
        // 啓動服務
        server.service();
    }
}

客戶端代碼爲:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package remoteProxy.Client;

import java.io.*;
import java.net.Socket;

/**
 * Created by Benjamin on 1/15/16.
 * Connector 類負責創建與遠程服務器的鏈接,以及接收和發送Socket對象
 */
public class Connector {

    private String host;
    private int port;
    private Socket skt;
    private InputStream is;
    private ObjectInputStream ois;
    private OutputStream os;
    private ObjectOutputStream oos;

    public Connector(String host, int port) throws Exception {
        this.host = host;
        this.port = port;
        connect(host, port);
    }

    /** 發送對象 **/
    public void send(Object obj) throws IOException {
        oos.writeObject(obj);
    }

    /** 接收對象 **/
    public Object receive() throws IOException, ClassNotFoundException {
        return ois.readObject();
    }

    /** 創建與遠程服務器的鏈接 **/
    public void connect() throws Exception {
        connect(host, port);
    }

    public void connect(String host, int port) throws Exception {
        skt = new Socket(host, port);
        os = skt.getOutputStream();
        oos = new ObjectOutputStream(os);
        is = skt.getInputStream();
        ois = new ObjectInputStream(is);
    }

    public void close() { // 關閉鏈接
        try {
        } finally {
            try {
                ois.close();
                oos.close();
                skt.close();
            } catch (Exception e) {
                System.out.println("Connector.close: " + e);
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package remoteProxy.Client;

import remoteProxy.Call;
import remoteProxy.IHelloService;

import java.rmi.RemoteException;
import java.util.Date;

/**
 * Created by piqiu on 1/15/16.
 */
public class HelloServiceProxy implements IHelloService {

    private String host;
    private int port;

    public HelloServiceProxy(String host, int port) {
        this.host = host;
        this.port = port;
    }

    public String echo(String msg) throws RemoteException {
        Connector connector = null;
        try {

            connector = new Connector(host, port);
            Call call = new Call("remoteProxy.IHelloService", "echo", new Class[]{String.class}, new Object[]{msg});
            connector.send(call);
            call = (Call)connector.receive();
            Object result = call.getResult();
            if (result instanceof Throwable) {
                throw new RemoteException("", (Throwable)result);
            } else {
                return (String)result;
            }
        } catch (Exception e) {
            throw new RemoteException("", e);
        } finally {
            if (connector != null) connector.close();
        }
    }

    public Date getTime() throws RemoteException {
        Connector connector = null;
        try {

            connector = new Connector(host, port);
            Call call = new Call("remoteProxy.IHelloService", "getTime", new Class[]{}, new Object[]{});
            connector.send(call);
            call = (Call)connector.receive();
            Object result = call.getResult();
            if (result instanceof Throwable) {
                throw new RemoteException("", (Throwable)result);
            } else {
                return (Date)result;
            }
        } catch (Exception e) {
            throw new RemoteException("", e);
        } finally {
            if (connector != null) connector.close();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package remoteProxy.Client;

import remoteProxy.Call;

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

/**
 * Created by piqiu on 1/15/16.
 */
public class ProxyFactory {

    public static Object getProxy(final Class classType, final String host, final int port) {
        InvocationHandler handler = new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Connector connector = null;
                try {
                    connector = new Connector(host, port);
                    Call call = new Call(classType.getName(), method.getName(),
                            method.getParameterTypes(), args);
                    connector.send(call);
                    call = (Call) connector.receive();
                    Object result = call.getResult();
                    if (result instanceof Throwable)
                        throw new RemoteException("",(Throwable) result); // 把異常都轉換爲RemoteException
                    else
                        return result;
                } finally {
                    if (connector != null)
                        connector.close();
                }
            }
        };
        return Proxy.newProxyInstance(classType.getClassLoader(), new Class[]{classType}, handler);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package remoteProxy.Client;

import remoteProxy.IHelloService;

import java.rmi.RemoteException;

/**
 * Created by piqiu on 1/15/16.
 */
public class SimpleClient {

    public static void main(String[] args) throws RemoteException {
        IHelloService helloService = new HelloServiceProxy("localhost", 8888);
        System.out.println(helloService.echo("hello"));
        System.out.println(helloService.getTime());

        IHelloService helloService1 = (IHelloService)ProxyFactory.getProxy(IHelloService.class, "localhost", 8888);
        System.out.println(helloService1.echo("hello2"));
        System.out.println(helloService.getTime());
    }
}

運行時候先運行服務端,再運行客戶端。結果爲:

服務端:

1
2
3
4
5
服務器啓動......
HelloServiceImpl.echo: hello
HelloServiceImpl.getTime: Fri Jan 15 14:32:52 CST 2016
HelloServiceImpl.echo: hello2
HelloServiceImpl.getTime: Fri Jan 15 14:32:52 CST 2016

客戶端:

1
2
3
4
hello
Fri Jan 15 14:32:52 CST 2016
hello2
Fri Jan 15 14:32:52 CST 2016

虛擬代理的實現

根據須要建立開銷很大的對象。經過它來存放實例化須要很長時間的真實對象。

安全代理的實現

用來控制真是對象訪問時的權限

智能指引

當調用真實的對象時,代理處理另一些事。如計算真實對象的引用次數,這樣當該對象沒有引用時,能夠自動釋放它。或當第一次引用一個持久對象時,將它裝入內存。或在訪問一個實例對象前,檢查是否已經鎖定它,以確保其餘對象不能改變它。他們都是經過代理在訪問一個對象時附加一些內務處理。

相關文章
相關標籤/搜索