動態(tài)代理在Android實(shí)際開發(fā)中用的并不是很多,但在設(shè)計(jì)框架的時候用的就比較多了,最近在看J2EE一些東西,像Spring,Hibernate等都有通過動態(tài)代理來實(shí)現(xiàn)方法增強(qiáng)、方法攔截等需要,通過代理的方式優(yōu)雅的實(shí)現(xiàn)AOP編程。我們今天來看看這個代理究竟是什么樣子,在Android開發(fā)中如何使用它,以及將cglib動態(tài)代理思想在Android中看看如何實(shí)現(xiàn)。
項(xiàng)目地址:MethodInterceptProxy
一、什么是代理
通常我們說的代理,在生活中就像中介、經(jīng)紀(jì)人的角色。
目標(biāo)對象/被代理對象 ------ 房主:真正的租房的方法
代理對象 ------- 黑中介:有租房子的方法(調(diào)用房主的租房的方法)
執(zhí)行代理對象方法的對象 ---- 租房的人
流程:我們要租房----->中介(租房的方法)------>房主(租房的方法)
抽象:調(diào)用對象----->代理對象------>目標(biāo)對象
二、靜態(tài)代理
先看看比較常見的靜態(tài)代理,也就是裝飾設(shè)計(jì)模式:
首先建一個Star接口:
public interface Star {
void singSong();
}
然后建Star子類SuperStar
public class SuperStar implements Star {
@Override
public void singSong() {
System.out.println("唱歌啦--------");
}
}
最后我們創(chuàng)建SuperStar的代理類SuperStarProxy
public class SuperStarProxy implements Star {
private Star star;
SuperStarProxy(Star star){
this.star = star;
}
@Override
public void singSong() {
System.out.println("before-------------");
star.singSong();
System.out.println("after-------------");
}
}
public static void main(String[] args) {
Star star = new SuperStarProxy(new SuperStar());
star.singSong();
}
我們將需要代理的對象傳進(jìn)來生成代理對象,之后只需要使用代理對象來處理相關(guān)業(yè)務(wù)就可以了。
三、動態(tài)代理
靜態(tài)代理需要為每一個需要代理的類寫一個代理類,為每一個需要代理的方法重寫代理方法,如果有上百個類或者類里方法很多,那重復(fù)的工作量也是很可觀的。JDK提供了動態(tài)代理方式,可以簡單理解為在JVM可以在運(yùn)行時幫我們動態(tài)生成一系列的代理類,這樣我們就不需要手寫每一個靜態(tài)的代理類了。
final Star star = new SuperStar();
Star st = (Star) Proxy.newProxyInstance(Star.class.getClassLoader(), star.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("before---------");
Object object = method.invoke(star, args);
System.out.println("after---------");
return object;
}
});
st.singSong();
newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
參數(shù)一:類加載器,動態(tài)代理類,運(yùn)行時創(chuàng)建。一般情況:當(dāng)前類.class.getClassLoader()
參數(shù)二:interfaces:代表與目標(biāo)對象實(shí)現(xiàn)的所有的接口字節(jié)碼對象數(shù)組
參數(shù)三:具體的代理的操作,InvocationHandler接口
通過JDK提供的動態(tài)代理方式,我們可以很輕松的生成代理對象,但通過這種方式實(shí)現(xiàn)代理有個很大的限制就是:JDK的Proxy方式實(shí)現(xiàn)的動態(tài)代理,目標(biāo)對象必須有接口,沒有接口不能實(shí)現(xiàn)jdk版動態(tài)代理。
四、cglib
cglib是一個功能強(qiáng)大,高性能的代碼生成包。它為沒有實(shí)現(xiàn)接口的類提供代理,為JDK的動態(tài)代理提供了很好的補(bǔ)充。通??梢允褂肑ava的動態(tài)代理創(chuàng)建代理,但當(dāng)要代理的類沒有實(shí)現(xiàn)接口或者為了更好的性能,cglib是一個好的選擇。
但是但是但是,一個很致命的缺點(diǎn)是:cglib底層采用ASM字節(jié)碼生成框架,使用字節(jié)碼技術(shù)生成代理類,也就是生成的.class文件,而我們在android中加載的是優(yōu)化后的.dex文件,也就是說我們需要可以動態(tài)生成.dex文件代理類,cglib在android中是不能使用的。但后面我們會根據(jù)dexmaker框架來仿照動態(tài)生成.dex文件,實(shí)現(xiàn)cglib的動態(tài)代理功能。
好了,我們先來看下cglib的強(qiáng)大吧~
舉個例子,boss安排要實(shí)現(xiàn)一個人員管理的增刪改查功能,那這個簡單,三兩下就搞定:
public class PeopleService {
public void add(){
System.out.println("add-----------");
}
public void delete(){
System.out.println("delete-----------");
}
public void update(){
System.out.println("update-----------");
}
public void select(){
System.out.println("select-----------");
}
}
OK,搞定~但是呢,需求是不斷變化的,過了幾天,boss又發(fā)話了,說不是每個人都可以使用這個增刪改查功能的,要指定人員才可以使用,那。。我們就改吧~最直接的方式,在每個方法上都加上判斷條件,但這么做多少有點(diǎn)太挫了,如果有五十個方法,那我們這么多方法就要都加一遍,用cglib我們可以這么做:
final String name = "張si";
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(PeopleService.class);
//目標(biāo)對象攔截器,實(shí)現(xiàn)MethodInterceptor
//Object object為目標(biāo)對象
//Method method為目標(biāo)方法
//Object[] args 為參數(shù),
//MethodProxy proxy CGlib方法代理對象
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object object, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
Object obj = null;
if(name.equals("張三")){
obj = proxy.invokeSuper(object, args); ;
}else{
System.out.println("----對不起,您沒有權(quán)限----");
}
return obj;
}
});
PeopleService ps = (PeopleService) enhancer.create();
ps.add();
只需添加一個攔截器,就可以攔截所有增刪改查操作,從切面進(jìn)行統(tǒng)一管理,代碼量也不多。
又過了幾天,boss又發(fā)話了,我們的增刪改查不是都要有限制,只有查找才對特定人員有限制,那我們就繼續(xù)改嘍~
這個時候我們就可以加入過濾器CallbackFilter:
final String name = "張武";
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(PeopleService.class);
//目標(biāo)對象攔截器,實(shí)現(xiàn)MethodInterceptor
//Object object為目標(biāo)對象
//Method method為目標(biāo)方法
//Object[] args 為參數(shù),
//MethodProxy proxy CGlib方法代理對象
MethodInterceptor interceptor = new MethodInterceptor() {
@Override
public Object intercept(Object object, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
Object obj = null;
if(name.equals("張三")){
obj = proxy.invokeSuper(object, args); ;
}else{
System.out.println("----對不起,您沒有權(quán)限----");
}
return obj;
}
};
//NoOp.INSTANCE:這個NoOp表示no operator,即什么操作也不做,代理類直接調(diào)用被代理的方法不進(jìn)行攔截
enhancer.setCallbacks(new Callback[]{interceptor,NoOp.INSTANCE});
enhancer.setCallbackFilter(new CallbackFilter() {
//過濾方法
//返回的值為數(shù)字,代表了Callback數(shù)組中的索引位置,要到用的Callback
@Override
public int accept(Method method) {
if(method.getName().equals("select")){
return 0;
}
return 1;
}
});
PeopleService ps = (PeopleService) enhancer.create();
ps.add();
我們沒有修改一行原有的增刪改查代碼,通過傳遞代理對象的方式輕松解決方法攔截、方法增強(qiáng)的業(yè)務(wù)需求。
但遺憾的是,cglib不支持android平臺。。。那我們就自己實(shí)現(xiàn)咯~在dexmaker和cglib-for-android庫的基礎(chǔ)上,修改部分代碼后形成我們的類似cglib框架 MethodInterceptProxy ,實(shí)現(xiàn)上面需求只需這樣寫,和cglib寫法一致:
final String name = "張五";
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(PeopleService.class);
//目標(biāo)對象攔截器,實(shí)現(xiàn)MethodInterceptor
//Object object為目標(biāo)對象
//Method method為目標(biāo)方法
//Object[] args 為參數(shù),
//MethodProxy proxy CGlib方法代理對象
MethodInterceptor interceptor = new MethodInterceptor() {
@Override
public Object intercept(Object object, Object[] args, MethodProxy methodProxy) throws Throwable {
Object obj = null;
if(name.equals("張三")){
obj = methodProxy.invokeSuper(object, args); ;
}else{
System.out.println("----對不起,您沒有權(quán)限----");
}
return obj;
}
};
//NoOp.INSTANCE:這個NoOp表示no operator,即什么操作也不做,代理類直接調(diào)用被代理的方法不進(jìn)行攔截
enhancer.setCallbacks(new MethodInterceptor[]{interceptor,NoOp.INSTANCE});
enhancer.setCallbackFilter(new CallbackFilter() {
//過濾方法
//返回的值為數(shù)字,代表了Callback數(shù)組中的索引位置,要到用的Callback
@Override
public int accept(Method method) {
if(method.getName().equals("select")){
return 0;
}
return 1;
}
});
PeopleService ps = (PeopleService) enhancer.create();
ps.add();
項(xiàng)目地址:MethodInterceptProxy