代理模式
1 靜態(tài)代理和動(dòng)態(tài)代理
代理的概念:為某個(gè)對(duì)象提供一個(gè)代理晴股,以控制對(duì)這個(gè)對(duì)象的訪問腰池。 代理類和委托類有共同的父類或父接口,這樣在任何使用委托類對(duì)象的地方都可以用代理對(duì)象替代。代理類負(fù)責(zé)請(qǐng)求的預(yù)處理端辱、過濾得哆、將請(qǐng)求分派給委托類處理脯颜、以及委托類執(zhí)行完請(qǐng)求后的后續(xù)處理。
白話文:簡(jiǎn)單的說贩据,就是類似于買東西栋操,廠商負(fù)責(zé)生產(chǎn),貨物交給商店來代理出售饱亮》剑客人跟商店來交互,但是其實(shí)買的是廠商的東西近上。
靜態(tài)代理
package com.proxy.inter;
/**
* 定義Demo接口
*/
public interface Demo {
public void save();
}
package com.proxy.impl;
import com.proxy.inter.Demo;
/**
* DemoImpl實(shí)現(xiàn)Demo接口并覆寫save()方法
* 真實(shí)主題剔宪,執(zhí)行具體業(yè)務(wù)
*/
public class DemoImpl implements Demo {
public void save() {
System.out.println("調(diào)用save()方法");
}
}
package com.proxy.impl;
import com.proxy.inter.Demo;
/**
* DemoImplProxy 也實(shí)現(xiàn)了Demo接口,并覆寫了save()方法壹无,增加了自己的業(yè)務(wù)
* 代理主題歼跟,負(fù)責(zé)其他業(yè)務(wù)的處理
*/
public class DemoImplProxy implements Demo {
Demo demoImpl = new DemoImpl();
public void save() {
System.out.println("開始記錄日志");
demoImpl.save();
System.out.println("開始結(jié)束日志");
}
}
package com.proxy.impl;
import com.proxy.inter.Demo;
/**
* 開始記錄日志
* 調(diào)用save()方法
* 開始結(jié)束日志
*/
public class Test {
public static void main(String[] args) {
Demo demoImplProxy = new DemoImplProxy();
demoImplProxy.save();
}
}
靜態(tài)代理有一個(gè)缺點(diǎn),每個(gè)代理類只能為一個(gè)接口服務(wù)格遭,這樣程序開發(fā)中必然會(huì)產(chǎn)生許多的代理類
所以我們就會(huì)想辦法可以通過一個(gè)代理類完成全部的代理功能哈街,那么我們就需要用動(dòng)態(tài)代理
在Java中要想實(shí)現(xiàn)動(dòng)態(tài)代理機(jī)制,需要java.lang.reflect.InvocationHandler 接口和 java.lang.reflect.Proxy 類的支持拒迅。
動(dòng)態(tài)代理
java.lang.reflect.InvocationHandler接口的定義如下:
/**
*
*Object proxy:被代理的對(duì)象
*Method method:要調(diào)用的方法
*Object[] args:方法調(diào)用時(shí)所需要參數(shù)
*/
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
java.lang.reflect.Proxy類的定義如下:
/**
*CLassLoader loader:類的加載器
*Class<?> interfaces:得到全部的接口
*InvocationHandler h:得到InvocationHandler接口的子類的實(shí)例
*/
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
具體實(shí)現(xiàn)
package com.proxy.inter;
/**
* 定義DemoFirst接口
*/
public interface DemoFirst {
public void saveFirst();
}
package com.proxy.impl;
import com.proxy.inter.DemoFirst;
/**
* DemoFirstImpl實(shí)現(xiàn)DemoFirst接口骚秦,覆寫saveFirst()方法
* 真實(shí)主題,負(fù)責(zé)執(zhí)行具體業(yè)務(wù)
*/
public class DemoFirstImpl implements DemoFirst {
@Override
public void saveFirst() {
System.out.println("調(diào)用saveFirst()方法");
}
}
package com.proxy.impl;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* InvocationHandlerImple實(shí)現(xiàn)InvocationHandler接口璧微,覆寫invoke()方法
* 代理主題的業(yè)務(wù)寫在invoke()方法中
*/
public class InvocationHandlerImpl implements InvocationHandler {
private Object target;
public InvocationHandlerImpl(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("target : " + target.getClass().getName());
System.out.println("proxy : " + proxy.getClass().getName());
System.out.println("method : " + method.getName());
System.out.println("args : " + args);
System.out.println("開始記錄日志");
Object obj = method.invoke(target, args);
System.out.println("結(jié)束記錄日志");
/*
* System.out.println("obj : " + obj.getClass().getName());
* 本例中saveXXX方法沒有返回值所以obj會(huì)報(bào)空指針異常
*/
return obj;
}
}
package com.proxy.impl;
import java.lang.reflect.Proxy;
import com.proxy.inter.DemoFirst;
/*import com.proxy.inter.DemoSecond;*/
public class Test {
public static void main(String[] args) {
DemoFirst first = new DemoFirstImpl();
/*DemoSecond second = new DemoSecondImpl();*/
//取得代理對(duì)象
DemoFirst firstProxy = (DemoFirst) Proxy.newProxyInstance(first
.getClass().getClassLoader(), first.getClass().getInterfaces(),
new InvocationHandlerImpl(first));
//通過動(dòng)態(tài)代理調(diào)用方法
firstProxy.saveFirst();
/*DemoSecond secondProxy = (DemoSecond) Proxy.newProxyInstance(second
.getClass().getClassLoader(), second.getClass().getInterfaces(),
new InvocationHandlerImpl(second));
secondProxy.saveSecond();*/
}
}
優(yōu)缺點(diǎn)
缺點(diǎn):
如果是代理類作箍,那么調(diào)用getclass等方法的時(shí)候,會(huì)無法得到預(yù)期的結(jié)果前硫。
代理模式的應(yīng)用
代理模式是非常好用的胞得,在很多的地方都有著用途。比如spring的AOP屹电,
代理模式有多種應(yīng)用場(chǎng)合阶剑,如下所述:
遠(yuǎn)程代理跃巡,也就是為一個(gè)對(duì)象在不同的地址空間提供局部代表,這樣可以隱藏一個(gè)對(duì)象存在于不同地址空間的事實(shí)牧愁。比如說 WebService素邪,當(dāng)我們?cè)趹?yīng)用程序的項(xiàng)目中加入一個(gè) Web 引用,引用一個(gè) WebService猪半,此時(shí)會(huì)在項(xiàng)目中聲稱一個(gè) WebReference 的文件夾和一些文件兔朦,這個(gè)就是起代理作用的,這樣可以讓那個(gè)客戶端程序調(diào)用代理解決遠(yuǎn)程訪問的問題磨确;
虛擬代理沽甥,是根據(jù)需要?jiǎng)?chuàng)建開銷很大的對(duì)象,通過它來存放實(shí)例化需要很長(zhǎng)時(shí)間的真實(shí)對(duì)象乏奥。這樣就可以達(dá)到性能的最優(yōu)化摆舟,比如打開一個(gè)網(wǎng)頁(yè),這個(gè)網(wǎng)頁(yè)里面包含了大量的文字和圖片英融,但我們可以很快看到文字盏檐,但是圖片卻是一張一張地下載后才能看到,那些未打開的圖片框驶悟,就是通過虛擬代里來替換了真實(shí)的圖片胡野,此時(shí)代理存儲(chǔ)了真實(shí)圖片的路徑和尺寸;
安全代理痕鳍,也可以是權(quán)限代理硫豆。用來控制真實(shí)對(duì)象訪問時(shí)的權(quán)限。一般用于對(duì)象應(yīng)該有不同的訪問權(quán)限的時(shí)候笼呆;
延遲加載熊响,用代理模式實(shí)現(xiàn)延遲加載的一個(gè)經(jīng)典應(yīng)用就在 Hibernate 框架里面。當(dāng) Hibernate 加載實(shí)體 bean 時(shí)诗赌,并不會(huì)一次性將數(shù)據(jù)庫(kù)所有的數(shù)據(jù)都裝載汗茄。默認(rèn)情況下,它會(huì)采取延遲加載的機(jī)制铭若,以提高系統(tǒng)的性能洪碳。Hibernate 中的延遲加載主要分為屬性的延遲加載和關(guān)聯(lián)表的延時(shí)加載兩類。實(shí)現(xiàn)原理是使用代理攔截原有的 getter 方法叼屠,在真正使用對(duì)象數(shù)據(jù)時(shí)才去數(shù)據(jù)庫(kù)或者其他第三方組件加載實(shí)際的數(shù)據(jù)瞳腌,從而提升系統(tǒng)性能。
利用代理模式封裝第三方網(wǎng)絡(luò)請(qǐng)求
代理模式就是:為其他對(duì)象提供一種代理镜雨,以控制對(duì)這個(gè)對(duì)象的訪問嫂侍。
舉個(gè)例子:沒空下去吃飯,找個(gè)同事幫忙買飯就是代理模式;平常租房子挑宠,
嫌麻煩菲盾,說出房子條件,讓找中介幫忙找痹栖,也是一種代理模式亿汞。
了解了代理模式瞭空,接下來的代碼就好理解了揪阿。
首先我們的使用場(chǎng)景:項(xiàng)目剛開始,使用的是Volley請(qǐng)求框架咆畏,基本需求
都能滿足南捂。后來,出現(xiàn)了更完美的OkHttp請(qǐng)求框架旧找,老板想讓你使用最新的框架溺健。
如果你在代碼里請(qǐng)求網(wǎng)絡(luò)的時(shí)候,都是直接使用的Volley代碼直接進(jìn)行操作的钮蛛,那
就需要把每一處的請(qǐng)求都改一遍鞭缭,非常麻煩。
代碼
統(tǒng)一網(wǎng)絡(luò)請(qǐng)求回調(diào):
/**
* 接口統(tǒng)一回調(diào)
* */
public interface ICallBack {
//成功
void onSuccess(String result);
// 失敗
void onFailed(String result);
}
代理接口:
/**
* 代理接口
* 封裝各種三方類庫(kù)的共同方法
* */
public interface IHttpProcessor {
// http 協(xié)議的四種動(dòng)作 get post put delete
void get(String url ,Map<String ,Object> params,ICallBack callBack);
void post(String url,Map<String ,Object> params,ICallBack callBack);
}
代理對(duì)象:類似于一個(gè)工具類魏颓,可以直接調(diào)用相關(guān)方法
/**
* 代理類
*/
public class HttpUtils implements IHttpProcessor {
private static IHttpProcessor httpProcessor;
private static HttpUtils httpUtils;
public HttpUtils() {
}
// 單例
public static HttpUtils getInstance() {
if (httpUtils == null) {
synchronized (HttpUtils.class) {
if (httpUtils == null)
httpUtils = new HttpUtils();
}
}
return httpUtils;
}
public static void init(IHttpProcessor processor) {
httpProcessor = processor;
}
@Override
public void get(String url, Map<String, Object> params, ICallBack callBack) {
httpProcessor.get(url, params, callBack);
}
@Override
public void post(String url, Map<String, Object> params, ICallBack callBack) {
httpProcessor.post(url, params, callBack);
}
}
委托對(duì)象:即三方網(wǎng)絡(luò)請(qǐng)求框架
首先岭辣,是Volley框架
public class VolleyProcessor implements IHttpProcessor {
private RequestQueue requestQueue;
public VolleyProcessor(Context context) {
requestQueue = Volley.newRequestQueue(context);
}
@Override
public void get(String url, Map<String, Object> params, final ICallBack callBack{
StringRequest request = new StringRequest(Request.Method.GET, url, new Listener<String>() {
@Override
public void onResponse(String response) {
callBack.onSuccess(response);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
// TODO Auto-generated method stub
callBack.onFailed(error.toString());
}
});
requestQueue.add(request);
}
@Override
public void post(String url, final Map<String, Object> params, final ICallBack callBack) {
StringRequest request = new StringRequest(url, new Listener<String>() {
@Override
public void onResponse(String response) {
callBack.onSuccess(response);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
// TODO Auto-generated method stub
callBack.onFailed(error.toString());
}
}) {
@Override
protected Map<String, String> getParams() throws AuthFailureError {
//將Map<String, Object> 轉(zhuǎn)為 <String, String>
Map<String, String> newMap = new HashMap<String, String>();
for (Map.Entry<String, Object> entry : params.entrySet()) {
newMap.put(entry.getKey(), (String) entry.getValue());
}
return newMap;
}
};
requestQueue.add(request);
}
}
然后是 Okhttp 框架:
public class OkHttpProcessor implements IHttpProcessor {
private OkHttpClient client;
private Handler mHandler;// okhttp 返回結(jié)果在子線程中,通過handler 發(fā)送到主線程
public OkHttpProcessor() {
client = new OkHttpClient();
mHandler = new Handler();
}
@Override
public void get(String url, Map<String,Object> params, final ICallBack callBack) {
// TODO Auto-generated method stub
Request request = new Request.Builder().get().url(url)
// 不添加甸饱,訪問中文網(wǎng)絡(luò)的時(shí)候回出錯(cuò)
.header("User-Agent", "a").build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onResponse(Call arg0, final Response arg1) throws IOException {
if (arg1.isSuccessful()) {
final String result = arg1.body().string();
mHandler.post(new Runnable() {
@Override
public void run() {
callBack.onSuccess(result);
}
});
} else {
mHandler.post(new Runnable() {
@Override
public void run() {
callBack.onFailed(arg1.toString());
}
});
}
}
@Override
public void onFailure(Call arg0, final IOException arg1) {
mHandler.post(new Runnable() {
@Override
public void run() {
callBack.onFailed(arg1.toString());
}
});
}
});
}
@Override
public void post(String url, Map<String, Object> params, final ICallBack callBack) {
Request request = new Request.Builder().post(appendyBody(params))
.url(url).header("User-Agent", "a")
.addHeader("", "a").build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onResponse(Call arg0, final Response arg1) throws IOException {
if (arg1.isSuccessful()) {
final String result = arg1.body().toString();
mHandler.post(new Runnable() {
@Override
public void run() {
callBack.onSuccess(result);
}
});
} else {
mHandler.post(new Runnable() {
@Override
public void run() {
callBack.onFailed(arg1.toString());
}
});
}
}
@Override
public void onFailure(Call arg0, final IOException arg1) {
mHandler.post(new Runnable() {
@Override
public void run() {
callBack.onFailed(arg1.toString());
}
});
}
});
}
/**
* 拼接參數(shù)
*/
public RequestBody appendyBody(Map<String, Object> params) {
FormBody.Builder builder = new FormBody.Builder();
if (params == null || params.isEmpty()) {
return builder.build();
}
for (Map.Entry<String, Object> map : params.entrySet()) {
builder.add(map.getKey(), map.getValue().toString());
}
return builder.build();
}
}
到這里代碼已經(jīng)寫完沦童。接下來就是見證奇跡的時(shí)刻,一行代碼修改網(wǎng)絡(luò)框架叹话。
自定義 MyApplication
public class MyApplication extends Application{
@Override
public void onCreate() {
super.onCreate();
//綁定委托對(duì)象
//
HttpUtils.init(new OkHttpProcessor());
HttpUtils.init(new VolleyProcessor(this));
}
}
紅色的標(biāo)記偷遗,即可做到自如的切換網(wǎng)絡(luò)請(qǐng)求框架。
如果想用OkHttp 即使用第一行驼壶,想用 Volley 使用第二行代碼氏豌。
代碼中使用: 例如 獲取 天氣信息
String url="http://www.weather.com.cn/data/cityinfo/101010100.html";
HttpUtils.getInstance().get(url, null, new ICallBack() {
@Override
public void onSuccess(String result) {
Log.d("log", "result=="+result);
}
@Override
public void onFailed(String result) {
Log.d("log",
"result=="+result);
}
});
注:Application類中是這個(gè)代碼中唯一代碼耦合的地方站蝠。