1. 程式人生 > >設計模式之代理模式(Proxy Pattern)

設計模式之代理模式(Proxy Pattern)

應用場景

Proxy代理模式是一種結構型設計模式,主要解決的問題是:直接訪問物件時帶來的問題。

為了保持行為的一致性,代理類和委託類通常會實現相同的介面,所以在訪問者看來兩者並無區別。

代理類種類:

靜態:建立代理類再對其編譯,在程式執行前代理類的.class檔案就已經存在了。

動態:在程式執行時運用反射機制動態建立而成。

一、靜態代理

靜態代理實現很簡單,就是跟目標類實現同樣的介面,然後引入目標類。在目標類方法執行前後增加額外的方法即可。

但是有個問題需要注意:每次在介面中新增一個新方法,則需要在目標物件中實現這個方法,並且在代理物件中實現相應的代理方法。

public interface ProductDao {
	//新增商品
	void add();
	//刪除商品
	void delete();
	//修改商品
	void update();
	//查詢商品
	void select();
}
public class ProductDaoImpl implements ProductDao {

	@Override
	public void add() {
		System.out.println("目標物件中新增商品");
	}

	@Override
	public void delete() {
		System.out.println("目標物件中刪除商品");
	}

	@Override
	public void update() {
		System.out.println("目標物件中修改商品");
	}

	@Override
	public void select() {
		System.out.println("目標物件中查詢商品");
	}

}
public class ProductDaoImplProxy implements ProductDao {

	private ProductDaoImpl productDaoImpl;

	public ProductDaoImplProxy(ProductDaoImpl productDaoImpl) {
		super();
		this.productDaoImpl = productDaoImpl;
	}

	@Override
	public void add() {
		System.out.println("新增商品begin");
		productDaoImpl.add();
	}

	@Override
	public void delete() {
		System.out.println("刪除商品begin");
		productDaoImpl.delete();
	}

	@Override
	public void update() {
		System.out.println("修改商品begin");
		productDaoImpl.update();
	}

	@Override
	public void select() {
		System.out.println("刪除商品");
		productDaoImpl.delete();
	}

}

 二、動態代理

常見的動態代理有JDK動態代理跟Cglib動態代理

1.JDK動態代理

JDK的動態代理,是使用反射技術獲得類的載入器並且建立例項,根據類執行的方法在執行方法的前後傳送通知。

java.lang.reflect 包下面的Proxy類和InvocationHandler 介面提供了生成動態代理類的能力。

在代理物件Proxy的新建代理例項方法中,必須要獲得類的載入器、類所實現的介面、還有一個攔截方法的控制代碼。然後在控制代碼的invoke()方法中呼叫method.invoke(),在此方法前後傳送通知,實現功能擴充套件。

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

public class ProductDaoJDKProxy {

	public static void main(String[] args) {
		//目標物件
		ProductDao productDao = new ProductDaoImpl();
		//代理物件
		ProductDao proxy = (ProductDao) Proxy.newProxyInstance(
				productDao.getClass().getClassLoader(),
				productDao.getClass().getInterfaces(), 
				new InvocationHandler() {
					//攔截到目標物件的方法時執行
					@Override
					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
						System.out.println("jdk在代理物件中攔截到方法:" + method.getName());
						proxy = method.invoke(productDao, args);
						return proxy;
					}
				});
		//代理物件呼叫方法
		proxy.add();
		proxy.delete();
	}

}

執行結果:

 

2.Cglib動態代理

JDK動態代理和靜態代理所代理的類都是實現了某介面的,對於沒有實現介面的類,可以使用Cglib動態代理。

Cglib動態代理需要引入兩個jar包:asm跟cglib

注意:arg0:生成的代理物件;arg1:目標物件的方法;arg2:目標物件的方法入參

import java.lang.reflect.Method;

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

public class ProductDaoCglibProxy {

	public static void main(String[] args) {
		ProductDaoImpl target = new ProductDaoImpl();
		Enhancer enhancer = new Enhancer();
		// 設定代理物件的父類
		enhancer.setSuperclass(target.getClass());
		// 設定回撥
		enhancer.setCallback(new MethodInterceptor() {
			@Override
			public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
				System.out.println("Cglib在代理物件中攔截到方法:" + arg1.getName());
				arg0 = arg1.invoke(target, arg2);
				return arg0;
			}
		});
		// 代理物件呼叫方法
		ProductDaoImpl proxy = (ProductDaoImpl) enhancer.create();
		proxy.add();
		proxy.delete();
	}

}

執行結果: