Java-設計模式-代理模式-靜態代理-動態代理-interface

靜態代理

參考

https://blog.csdn.net/justloveyou_/article/details/74203025
https://blog.csdn.net/briblue/article/details/73928350

根據代理類的產生方式和時機分爲靜態代理和動態代理兩種。代理類不僅可以有效的將具體的實現與調用方進行解耦,通過面向接口進行編碼完全將具體的實現隱藏在內部,而且還可以在符合開閉原則的前提下,對目標類進行進一步的增強。典型地,Spring AOP 是對JDK動態代理的經典應用。

例一

/*
 * 接口的應用:代理模式
 * 
 */
public class NetWorkTest {
	public static void main(String[] args) {
		Server server = new Server();
		// server.browse();
		ProxyServer proxyServer = new ProxyServer(server);
		// 外面是代理類執行的方法,其實是對真正工作的方法的增強
		proxyServer.browse();
	}
}

//接口有這個行爲或者功能
interface NetWork {
	public void browse();
}

// 被代理類 真正要去工作的這個類
class Server implements NetWork {
	@Override
	public void browse() {
		System.out.println("真實的服務器訪問網絡");
	}
}

// 代理類   代理了真實工作的類  對真正工作的方法進行了增強
class ProxyServer implements NetWork {
	private NetWork work;

	// 這裏的參數work是接口 其實傳Server也行  多態的體現
	public ProxyServer(NetWork work) {
		this.work = work;
	}

	public void check() {
		System.out.println("聯網之前的檢查工作");
	}

	@Override
	public void browse() {
		check();
		work.browse();
	}
}

例二

package com.atguigu.java1;

public class StaticProxyTest {

	public static void main(String[] args) {
		Proxy s = new Proxy(new RealStar());
		s.confer();
		s.signContract();
		s.bookTicket();
		s.sing();
		s.collectMoney();
	}
}

//接口定義所擁有的能力
interface Star {
	void confer();// 面談

	void signContract();// 籤合同

	void bookTicket();// 訂票

	void sing();// 唱歌

	void collectMoney();// 收錢
}

// 被代理類
class RealStar implements Star {
	public void confer() {
	}

	public void signContract() {
	}

	public void bookTicket() {
	}

	public void sing() {
		System.out.println("明星:歌唱~~~");
	}

	public void collectMoney() {
	}
}

// 代理類
class Proxy implements Star {
	private Star real;

	public Proxy(Star real) {
		this.real = real;
	}

	public void confer() {
		System.out.println("經紀人面談");
	}

	public void signContract() {
		System.out.println("經紀人籤合同");
	}

	public void bookTicket() {
		System.out.println("經紀人訂票");
	}

	public void sing() {
		real.sing();
	}

	public void collectMoney() {
		System.out.println("經紀人收錢");
	}
}

小結

朋友圈賣面膜的舉例,按理說,顧客可以直接從廠家購買產品,但是現實生活中,很少有這樣的銷售模式。一般都是廠家委託給代理商進行銷售,顧客跟代理商打交道,而不直接與產品實際生產者進行關聯。
所以,代理就有一種中間人的味道。
在這裏插入圖片描述
在這裏插入圖片描述需要注意的有下面幾點:

  • 用戶只關心接口功能,而不在乎誰提供了功能。上圖中接口是 Subject。
  • 接口真正實現者是上圖的 RealSubject,但是它不與用戶直接接觸,而是通過代理。
  • 代理就是上圖中的 Proxy,由於它實現了 Subject 接口,所以它能夠直接與用戶接觸。
  • 用戶調用 Proxy 的時候,Proxy 內部調用了 RealSubject。所以,Proxy 是中介者,它可以增強 RealSubject 操作。

例三

這裏再加上看電影的例子
https://blog.csdn.net/briblue/article/details/73928350

package com.atguigu.java1;

public class MovieProxy {
	public static void main(String[] args) {
		RealMovie realmovie = new RealMovie();
		Movie movie = new Cinema(realmovie);
		movie.play();

	}
}

// 首先得有一個接口,通用的接口是代理模式實現的基礎。這個接口我們命名爲 Movie,代表電影播放的能力。
interface Movie {
	void play();
}

// 然後,我們要有一個真正的實現這個 Movie 接口的類,和一個只是實現接口的代理類。
class RealMovie implements Movie {
	@Override
	public void play() {
		System.out.println("您正在觀看電影 《肖申克的救贖》");
	}
}

// 它實現了 Movie 接口, Cinema 就是Proxy 代理對象,
// 它有一個 play() 方法。
// 不過調用 play() 方法時,它進行了一些相關利益的處理,那就是廣告。現在,我們編寫測試代碼。
class Cinema implements Movie {

	RealMovie movie;

	public Cinema(RealMovie movie) {
		super();
		this.movie = movie;
	}

	@Override
	public void play() {

		guanggao(true);

		movie.play();

		guanggao(false);
	}

	public void guanggao(boolean isStart) {
		if (isStart) {
			System.out.println("電影馬上開始了,爆米花、可樂、口香糖9.8折,快來買啊!");
		} else {
			System.out.println("電影馬上結束了,爆米花、可樂、口香糖9.8折,買回家喫吧!");
		}
	}
}

靜態代理總結

現在可以看到,代理模式可以在不修改被代理對象的基礎上,通過擴展代理類,進行一些功能的附加與增強。值得注意的是,代理類和被代理類應該共同實現一個接口,或者是共同繼承某個類。代理類增強了被代理類的方法。代理類在執行之前或者之後做了其他的操作。

動態代理

在第一節我們已經提到,動態代理可以在程序運行期間根據需要動態的創建代理類及其實例來完成具體的功能,下面我們結合具體實例來介紹JDK動態代理。
看代碼註釋加理解

package com.atguigu.java1;

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

// 同樣地,首先得有一個接口,通用的接口是代理模式實現的基礎。
interface Subject {
	public void doSomething();
}

// 被代理角色(目標類)
// 然後,我們要有一個真正的實現這個 Subject 接口的類,以便代理。
class RealSubject implements Subject {
	public void doSomething() {
		System.out.println("call doSomething()");
	}
}

// 代理角色(代理類)與客戶端
// 在動態代理中,代理類及其實例是程序自動生成的,因此我們不需要手動去創建代理類。
// 在Java的動態代理機制中,InvocationHandler(Interface)接口和Proxy(Class)類是實現我們動態代理所必須用到的。
// 事實上,Proxy通過使用InvocationHandler對象生成具體的代理代理對象,下面我們看一下對InvocationHandler接口的實現:
class ProxyHandler implements InvocationHandler {

	private Object proxied; // 被代理對象

	public ProxyHandler(Object proxied) {
		this.proxied = proxied;
	}

	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		// 在轉調具體目標對象之前,可以執行一些功能處理
		System.out.println("前置增強處理: yoyoyo...");

		// 轉調具體目標對象的方法(三要素:實例對象 + 實例方法 + 實例方法的參數)
		Object obj = method.invoke(proxied, args);

		// 在轉調具體目標對象之後,可以執行一些功能處理
		System.out.println("後置增強處理:hahaha...");

		return obj;
	}
}

// 在實現了InvocationHandler接口後,我們就可以創建代理對象了。
// 在Java的動態代理機制中,我們使用Proxy類的靜態方法newProxyInstance創建代理對象,如下
public class DynamicProxy {
	public static void main(String args[]) {

		// 真實對象real 這都是多態的體現   類似於父類的引用指向子類的對象
		Subject real = new RealSubject();

		// 生成real的代理對象
		Subject proxySubject = (Subject) Proxy.newProxyInstance(Subject.class.getClassLoader(),
				new Class[] { Subject.class }, new ProxyHandler(real));

		proxySubject.doSomething();
		System.out.println("代理對象的類型 : " + proxySubject.getClass().getName());
		System.out.println("代理對象所在類的父類型 : " + proxySubject.getClass().getGenericSuperclass());
	}
}

到此爲止,我們給出了完整的基於JDK動態代理機制的代理模式的實現。我們從上面的實例中可以看到,代理對象proxySubject的類型爲”com.sun.proxy.$Proxy0”,這恰好印證了proxySubject對象是一個代理對象。除此之外,我們還發現代理對象proxySubject所對應的類繼承自java.lang.reflect.Proxy類,這也正是JDK動態代理機制無法實現對class的動態代理的原因:Java只允許單繼承。

JDK中InvocationHandler接口與Proxy類

(1). InvocationHandler接口

InvocationHandler 是一個接口,官方文檔解釋說:每個代理的實例都有一個與之關聯的 InvocationHandler 實現類,如果代理的方法被調用,那麼代理便會通知和轉發給內部的 InvocationHandler 實現類,由它決定處理。

public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

InvocationHandler中的invoke() 方法決定了怎麼樣處理代理傳遞過來的方法調用。

(2). Proxy類

JDK通過 Proxy 的靜態方法 newProxyInstance 動態地創建代理,該方法在Java中的聲明如下:

    /**     
     * @description 
     * @author rico       
     * @created 2017年7月3日 下午3:16:49     
     * @param loader 類加載器
     * @param interfaces 目標類所實現的接口
     * @param h  InvocationHandler 實例
     * @return     
     */
    public static Object newProxyInstance(ClassLoader loader,
            Class<?>[] interfaces,
            InvocationHandler h)

事實上,Proxy 動態產生的代理對象調用目標方法時,代理對象會調用 InvocationHandler 實現類,所以 InvocationHandler 是實際執行者。

Spring AOP 與 動態代理

AOP 專門用於處理系統中分佈於各個模塊(不同方法)中的交叉關注點的問題,在 Java EE 應用中,常常通過 AOP 來處理一些具有橫切性質的系統級服務,如事務管理、安全檢查、緩存、對象池管理等,AOP 已經成爲一種非常常用的解決方案。

AOP機制是 Spring 所提供的核心功能之一,其既是Java動態代理機制的經典應用,也是動態AOP實現的代表。Spring AOP默認使用Java動態代理來創建AOP代理,具體通過以下幾個步驟來完成:

Spring IOC 容器創建Bean(目標類對象);

Bean創建完成後,Bean後處理器(BeanPostProcessor)根據具體的切面邏輯及Bean本身使用Java動態代理技術生成代理對象;

應用程序使用上述生成的代理對象替代原對象來完成業務邏輯,從而達到增強處理的目的。

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