生活中有不少例子是間接來控制和訪問的,好比你找一我的不本身親自去,而是讓別人代替去作這就是最簡單的代理模式,是一種間接通訊的例子,對象間的間接通訊也一樣是面向對象設計中的一條重要的「審美觀」。間接通訊可讓對象間耦合性下降,以及易於複用的架構設計。java
間接控制對象的交互是一個重要的編程思想,有不少的模式都體現了這種思想,好比裝飾模式、適配器模式、代理模式,都是經過間接的方式實現某一目的。編程
這裏主要介紹一下代理模式,不管是在現實生活中仍是計算機技術中用到代理的地方很是多,主要分爲靜態代理和動態代理。數組
咱們都作過機房收費系統就那這個系統來舉例子,這個系統中有對用戶操做的用戶接口IUser,以及實現了這個接口的類UserImp,Java代碼以下。架構
/**
* 用戶表接口
* @author LLS
*
*/
public interface IUser
{
//添加用戶
void addUser();
//刪除用戶
void delUser();
}
用戶實現類ide
/**
* 實現用戶接口類
* @author LLS
*
*/
public class UserImpl implements IUser
{
public void addUser() {
// 添加用戶代碼
}
public void delUser() {
// 刪除用戶代碼
}
}
在這個例子中,咱們可能須要在添加用戶或者刪除用戶的時候進行權限檢查,符合權限的才能執行相關動做,不然不能執行,那麼該如何修改代碼才能更加貼切,並且在實際的編寫過程當中,雖然咱們須要權限模塊,但有時候爲了更好地快速測試,咱們經常但願暫時關閉權限模塊,如何才能讓這樣的臨時需求變得更加容易處理呢?咱們如今使用代理模式來完成這樣的任務,如今繼續編寫一個類叫 UserImplProxy.函數
用戶代理類post
/**
* 用戶實現類的代理
* @author LLS
*
*/
public class UserImplProxy implements IUser
{
//對用戶實現類的引用
private UserImpl userImpl;
//添加用戶
public void addUser()
{
//調用添加以前進行權限驗證
preIdentify();
if( userImpl == null )
{
userImpl = new UserImpl();
}
//調用源對象的添加
userImpl.addUser();
//添加完後,執行操做
postIdentify();
}
public void delUser()
{
preIdentify();
if( userImpl == null )
{
userImpl = new UserImpl();
}
userImpl.addUser();
postIdentify();
}
//驗證方法
private void preIdentify()
{
System.out.println("添加以前驗證代碼!");
}
//驗證
private void postIdentify()
{
System.out.println("添加後執行操做");
}
}
這樣就能夠很容易的實現權限驗證功能,很靈活。測試
可是問題又出現了,若是還有IStudent、ICard、IOnline……等不少接口,也許要一樣的權限驗證,是否是還要再爲每個接口都寫一個代理類嗎?this
固然不是了,這個時候就須要用到動態代理了,動態代理模式能夠在程序運行時爲不少類作代理。spa
靜態代理不足:一個被代理類對應一個代理類,當被代理類增多時,代理類會變多從而增長系統耦合度。
爲了提升類的複用性和系統設計靈活性,使得代碼更簡潔,能夠提取高層抽象類或接口。
Java提供了一個接口Java.lang.reflect.InvocationHandler和Proxy類支持動態代理,首先,介紹一下Proxy類,它有一個方法Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) ,這個方法返回一個代理類的對象。
ClassLoader loader:指定被代理對象的類加載器。
Class[] interfaces: 指定被代理對象所實現的接口。
InvocationHandler h:指定須要調用的InvocationHandler對象。
Java.lang.reflect.InvocationHandler接口,它只有一個方法invoke(),爲代理類的抽象方法。有三個參數
Object proxy :代理類對象
Method method :被代理對象的方法
Object[] args :該方法的參數數組
JDK中實現原理
1.產生代理類$Proxy0類
執行了Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)
將產生$Proxy0類,它繼承Proxy對象,並根據第二個參數,實現了被代理類的全部接口,天然就能夠生成接口要實現的全部方法了(這時候會重寫hashcode,toString和equals三個方法),可是尚未具體的實現體;
2.將代理類$Proxy0類加載到JVM中
這時候是根據Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)它的第一個參數----就是被代理類的類加載器,把當前的代理類加載到JVM中;
3.建立代理類$Proxy0類的對象
調用的$Proxy0類的$Proxy0(InvocationHandler)構造函數,生成$Proxy0類的對象。參數就是Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)它的第三個參數。這個參數就是咱們本身實現的InvocationHandler對象,咱們知道InvocationHandler對象中組合加入了代理類所代理的接口類的實現類;因此,$Proxy0對象調用全部要實現的接口的方法,都會調用InvocationHandler對象的invoke()方法實現。
咱們增長ICard接口和Card類
package com.proxy; /** * 卡接口 * @author LLS * */ public interface ICard { /** * 註冊卡號 */ public void registerCard(); /** * 註銷卡號 */ public void cancelCard(); }
Card實現類
package com.proxy;
public class CardImpl implements ICard {
@Override
public void registerCard() {
System.out.println("卡註冊類");
}
@Override
public void cancelCard() {
System.out.println("卡取消類");
}
}
動態代理類DynamicProxy
package com.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 動態生成代理類
* @author LLS
*
*/
public class DynamicProxy implements InvocationHandler {
/**
* 對要代理對象的引用
*/
private Object object=null;
/**
* 給引用複製
* @param object
*/
public DynamicProxy(Object object)
{
this.object=object;
}
/**
*
* @return
*/
public Object newProxyInstance(){
return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), this);
}
/**
* 經過代理對象,執行被代理對象的方法
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//進行權限驗證
System.out.println("執行操做以前先進行權限驗證,若是權限符合,則執行操做!");
Object res = method.invoke(object, args);
return res;
}
}
客戶端測試類
package com.proxy;
/**
* 客戶端類
* @author LLS
*
*/
public class Client
{
static public void main(String[] args)
{
//實例化被代理對象
UserImpl userImpl=new UserImpl();
//實例化一個產生代理對象的類
DynamicProxy dynamicProxyUser=new DynamicProxy(userImpl);
//獲得代理類對象
IUser proxyUser=(IUser)dynamicProxyUser.newProxyInstance();
//經過代理調用用戶添加方法
proxyUser.addUser();
//同上
CardImpl cardImpl=new CardImpl();
DynamicProxy dynamicProxyCard=new DynamicProxy(cardImpl);
ICard proxyCard=(ICard)dynamicProxyCard.newProxyInstance();
//經過代理調用,註冊卡號方法
proxyCard.registerCard();
}
}
運行結果爲:
動態代理有點像多態同樣,能夠在程序運行時決定實例化哪個對象,多態是利用接口向上轉型來實現,以爲也能夠用反射來實現,Java中的反射是一重要機制,是不少問題變得靈活,若是對反射原理比較熟悉,那麼理解不少別的東西也會容易理解一些。