只要是寫Java的贱除,動(dòng)態(tài)代理就一個(gè)必須掌握的知識(shí)點(diǎn)生闲,當(dāng)然剛開(kāi)始接觸的時(shí)候,理解的肯定比較淺勘伺,漸漸的會(huì)深入一些,這篇文章通過(guò)實(shí)戰(zhàn)例子幫助大家深入理解動(dòng)態(tài)代理飞醉。
說(shuō)動(dòng)態(tài)代理之前冲茸,要先搞明白什么是代理缅帘,代理的字面意思已經(jīng)很容易理解了轴术,我們這里撇開(kāi)其他的解釋,我們只談設(shè)計(jì)模式中的代理模式
什么是代理模式(Proxy)
定義:給目標(biāo)對(duì)象提供一個(gè)代理對(duì)象逗栽,并由代理對(duì)象控制對(duì)目標(biāo)對(duì)象的引用
在代理模式中,是需要代理對(duì)象和目標(biāo)對(duì)象實(shí)現(xiàn)同一個(gè)接口(如果是不同的接口失暂,那就是適配器模式了)彼宠,看下面的UML圖
為什么要用代理
最最最主要的原因就是弟塞,在不改變目標(biāo)對(duì)象方法的情況下對(duì)方法進(jìn)行增強(qiáng)凭峡,比如决记,我們希望對(duì)方法的調(diào)用增加日志記錄摧冀,或者對(duì)方法的調(diào)用進(jìn)行攔截,等等...
舉一個(gè)例子
現(xiàn)有一個(gè)IPerson接口索昂,只有一個(gè)方法say()
public interface IPerson {
void say();
}
有一個(gè)Man類,實(shí)現(xiàn)了IPerson
public class Man implements IPerson{
@Override
public void say() {
L.d("man say");
}
}
現(xiàn)在需要在say方法被調(diào)用的時(shí)候扩借,記錄方法被調(diào)用的時(shí)間,最直接的就是修改Man的say方法潮罪,但是這樣做的弊端就是如果有很多實(shí)現(xiàn)了IPerson接口的類康谆,那就需要修改多處代碼凄杯,而且這樣的修改可能會(huì)導(dǎo)致其他的代碼出問(wèn)題(可能并不是所有的say都需要記錄調(diào)用時(shí)間)秉宿。怎么辦呢戒突,這時(shí)候代理就要登場(chǎng)了描睦!
靜態(tài)代理
public class ManProxy implements IPerson{
private IPerson target;
public IPerson getTarget() {
return target;
}
public ManProxy setTarget(IPerson target) {
this.target = target;
return this;
}
@Override
public void say() {
if (target != null) {
L.d("man say invoked at : " + System.currentTimeMillis());
target.say();
}
}
}
這樣我們需要新建一個(gè)ManProxy類同樣實(shí)現(xiàn)IPerson接口膊存,將要代理的對(duì)象傳遞進(jìn)來(lái),這樣就可以在不修改Man的say方法的情況下實(shí)現(xiàn)了我們的需求隔崎。這其實(shí)就是靜態(tài)代理。那你可能要問(wèn)韵丑,既然有了靜態(tài)代理爵卒,為什么需要?jiǎng)討B(tài)代理呢,因?yàn)殪o態(tài)代理有一個(gè)最大的缺陷:接口與代理類是1對(duì)1的撵彻,有多個(gè)接口需要代理钓株,就需要新建多個(gè)代理類,繁瑣陌僵,類爆炸轴合。
動(dòng)態(tài)代理
我們先嘗試用動(dòng)態(tài)代理來(lái)解決上面的問(wèn)題。先新建一個(gè)類實(shí)現(xiàn)InvocationHandler碗短,
public class NormalHandler implements InvocationHandler {
private Object target;
public NormalHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
L.d("man say invoked at : " + System.currentTimeMillis());
method.invoke(target, args);
return null;
}
}
然后可以這樣使用
Man man = new Man();
NormalHandler normalHandler = new NormalHandler(man);
AnnotationHandler annotationHandler = new AnnotationHandler();
IPerson iPerson = (IPerson) Proxy.newProxyInstance(IPerson.class.getClassLoader(),
new Class[] {IPerson.class, IAnimal.class}, annotationHandler);
iPerson.say();
可以看到NormalHandler中代理的對(duì)象是Object類型受葛,所以它是被多個(gè)接口代理復(fù)用的,這樣就解決靜態(tài)代理類爆炸偎谁,維護(hù)困難的問(wèn)題总滩。我們重點(diǎn)看NormalHandler中的invoke方法,第二個(gè)參數(shù)method就是我們實(shí)際調(diào)用時(shí)的方法巡雨,所以動(dòng)態(tài)代理使用了反射闰渔,為了靈活稍稍犧牲一點(diǎn)性能。
動(dòng)態(tài)代理的成功案例
- Square公司出品的Android Restful 網(wǎng)絡(luò)請(qǐng)求庫(kù)Retrofit
- Spring AOP (默認(rèn)使用動(dòng)態(tài)代理鸯隅,如果沒(méi)有實(shí)現(xiàn)接口則使用CGLIB修改字節(jié)碼)
這2個(gè)庫(kù)不用多說(shuō)了向挖,Github上面Star數(shù)都是好幾萬(wàn)的網(wǎng)紅項(xiàng)目蝌以。
利用動(dòng)態(tài)代理實(shí)現(xiàn)一個(gè)低配的Retrofit
“talk is cheap, show me the code”, 所以捋起袖子干起來(lái)何之。
先新建需要用到的注解類和實(shí)體類
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface GET {
String value();
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface POST {
String value();
}
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface Query {
String value();
}
//更新實(shí)體類
public class CheckUpdate {
private boolean hasUpdate;
private String newVersion;
public boolean isHasUpdate() {
return hasUpdate;
}
public void setHasUpdate(boolean hasUpdate) {
this.hasUpdate = hasUpdate;
}
public String getNewVersion() {
return newVersion;
}
public void setNewVersion(String newVersion) {
this.newVersion = newVersion;
}
@Override
public String toString() {
return "Has update : " + hasUpdate + " ; The newest version is : " + newVersion;
}
}
接下來(lái)是接口方法類, 接口url地址這里隨便寫的跟畅,大家知道意思就OK了溶推。
public interface ApiService {
@POST("http://www.baidu.com/login")
Observable<User> login(@Query("username") String username, @Query("password") String password);
@GET("http://www.baidu.com/checkupdate")
Observable<CheckUpdate> checkUpdate(@Query("version") String version);
}
接下來(lái)就是我們的重點(diǎn)代理類RequestHandler徊件,里面的核心是解析方法注解的返回值和參數(shù)奸攻,包括返回值的泛型虱痕,在Json反序列化的時(shí)候回用到
public class RequestHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Annotation[] annotations = method.getAnnotations();
if (annotations != null && annotations.length > 0) {
Annotation annotation = annotations[0];
if (annotation instanceof GET) {
GET get = (GET) annotation;
return handleGetRequest(method, get, args);
}else if (annotation instanceof POST) {
POST post = (POST) annotation;
return handlePostRequest(method, post, args);
}
}
return null;
}
private Observable handleGetRequest(Method method, GET get, Object[] params) {
String url = get.value();
Type genericType = method.getGenericReturnType();
Parameter[] parameters = method.getParameters();
ParameterizedType parameterizedType = (ParameterizedType) genericType;
Class returnGenericClazz = null;
//解析方法返回值的參數(shù)類型
if (parameterizedType != null) {
Type[] types = parameterizedType.getActualTypeArguments();
for (int i = 0; i < types.length; i++) {
Class cls = (Class) types[i];
returnGenericClazz = cls;
break;
}
}
//解析請(qǐng)求參數(shù),然后拼接到url
if (params != null) {
url += "?";
for (int i = 0; i < params.length; i++) {
Query query = parameters[i].getAnnotation(Query.class);
url += query.value() + "=" + params[0].toString();
if (i < params.length - 1) {
url += "&";
}
}
}
final String getUrl = url;
final Class returnClazz = returnGenericClazz;
return Observable.create(observableEmitter -> {
Request request = new Request.Builder().url(getUrl).build();
Response response = new OkHttpClient()
.newCall(request).execute();
if (response.isSuccessful()) {
// String responseStr = response.body().string();
//這里mock返回?cái)?shù)據(jù)
String responseStr = MockFactory.mockCheckUpdateStr();
Object result = new Gson().fromJson(responseStr, returnClazz);
observableEmitter.onNext(result);
}else {
observableEmitter.onError(new IllegalStateException("http request failed!"));
}
observableEmitter.onComplete();
});
}
private Observable handlePostRequest(Method method, POST post, Object[] params) {
//篇幅關(guān)系部翘,這里省略硝训,可以參考get 實(shí)現(xiàn)
//。窖梁。。夹囚。。
}
}
新建一個(gè)門面類Retrofit荸哟,方便調(diào)用
public class Retrofit {
public static <T> T newProxy(Class<T> clazz) {
return (T) Proxy.newProxyInstance(clazz.getClassLoader(),
new Class[] {clazz}, new RequestHandler());
}
}
一個(gè)低配版的Retrofit就完成了假哎,趕緊去測(cè)試一下
public static void main(String[] args) {
ApiService apiService = ApiService apiService = Retrofit.newProxy(ApiService.class);
Observable<CheckUpdate> checkUpdateObservable = apiService.checkUpdate("3.1.0");
checkUpdateObservable.subscribeOn(Schedulers.io())
.subscribe(checkUpdate -> L.d(checkUpdate.toString()),
throwable -> L.d(throwable.getMessage()));
//等待工作線程執(zhí)行完成
Scanner sc = new Scanner(System.in);
if (sc.next() != null) {}
}
最終的執(zhí)行結(jié)果鞍历,當(dāng)然這里只是初步實(shí)現(xiàn)了Retrofit的一點(diǎn)點(diǎn)功能位谋,我們的目標(biāo)還是講解動(dòng)態(tài)代理這個(gè)技術(shù),以及它能夠干什么
最后一點(diǎn)小Tip
可以看到掏父,我們上面的低配的Retrofit,并沒(méi)有被代理的類秆剪,因?yàn)槲覀儍H僅通過(guò)解析ApiService接口中的注解中的信息已經(jīng)足夠我們?nèi)グl(fā)起Http請(qǐng)求,所以技術(shù)在于靈活運(yùn)用仅讽。
好了陶缺,這篇先到這里洁灵,大家開(kāi)心發(fā)財(cái)饱岸!