動(dòng)態(tài)代理在Android實(shí)際開(kāi)發(fā)中用的并不是很多,但在設(shè)計(jì)框架的時(shí)候用的就比較多了屠缭,最近在看J2EE一些東西箍鼓,像Spring,Hibernate等都有通過(guò)動(dòng)態(tài)代理來(lái)實(shí)現(xiàn)方法增強(qiáng)呵曹、方法攔截等需要袄秩,通過(guò)代理的方式優(yōu)雅的實(shí)現(xiàn)AOP編程。我們今天來(lái)看看這個(gè)代理究竟是什么樣子,在Android開(kāi)發(fā)中如何使用它之剧,以及將cglib動(dòng)態(tài)代理思想在Android中看看如何實(shí)現(xiàn)郭卫。
項(xiàng)目地址:MethodInterceptProxy
一、什么是代理
通常我們說(shuō)的代理背稼,在生活中就像中介贰军、經(jīng)紀(jì)人的角色。
目標(biāo)對(duì)象/被代理對(duì)象 ------ 房主:真正的租房的方法
代理對(duì)象 ------- 黑中介:有租房子的方法(調(diào)用房主的租房的方法)
執(zhí)行代理對(duì)象方法的對(duì)象 ---- 租房的人
流程:我們要租房----->中介(租房的方法)------>房主(租房的方法)
抽象:調(diào)用對(duì)象----->代理對(duì)象------>目標(biāo)對(duì)象
二蟹肘、靜態(tài)代理
先看看比較常見(jiàn)的靜態(tài)代理词疼,也就是裝飾設(shè)計(jì)模式:
首先建一個(gè)Star接口:
public interface Star {
void singSong();
}
然后建Star子類(lèi)SuperStar
public class SuperStar implements Star {
@Override
public void singSong() {
System.out.println("唱歌啦--------");
}
}
最后我們創(chuàng)建SuperStar的代理類(lèi)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();
}
我們將需要代理的對(duì)象傳進(jìn)來(lái)生成代理對(duì)象,之后只需要使用代理對(duì)象來(lái)處理相關(guān)業(yè)務(wù)就可以了帘腹。
三贰盗、動(dòng)態(tài)代理
靜態(tài)代理需要為每一個(gè)需要代理的類(lèi)寫(xiě)一個(gè)代理類(lèi),為每一個(gè)需要代理的方法重寫(xiě)代理方法阳欲,如果有上百個(gè)類(lèi)或者類(lèi)里方法很多舵盈,那重復(fù)的工作量也是很可觀(guān)的。JDK提供了動(dòng)態(tài)代理方式球化,可以簡(jiǎn)單理解為在JVM可以在運(yùn)行時(shí)幫我們動(dòng)態(tài)生成一系列的代理類(lèi)秽晚,這樣我們就不需要手寫(xiě)每一個(gè)靜態(tài)的代理類(lè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ù)一:類(lèi)加載器筒愚,動(dòng)態(tài)代理類(lèi)赴蝇,運(yùn)行時(shí)創(chuàng)建。一般情況:當(dāng)前類(lèi).class.getClassLoader()
參數(shù)二:interfaces:代表與目標(biāo)對(duì)象實(shí)現(xiàn)的所有的接口字節(jié)碼對(duì)象數(shù)組
參數(shù)三:具體的代理的操作巢掺,InvocationHandler接口
通過(guò)JDK提供的動(dòng)態(tài)代理方式句伶,我們可以很輕松的生成代理對(duì)象,但通過(guò)這種方式實(shí)現(xiàn)代理有個(gè)很大的限制就是:JDK的Proxy方式實(shí)現(xiàn)的動(dòng)態(tài)代理陆淀,目標(biāo)對(duì)象必須有接口熄阻,沒(méi)有接口不能實(shí)現(xiàn)jdk版動(dòng)態(tài)代理。
四倔约、cglib
cglib是一個(gè)功能強(qiáng)大,高性能的代碼生成包坝初。它為沒(méi)有實(shí)現(xiàn)接口的類(lèi)提供代理浸剩,為JDK的動(dòng)態(tài)代理提供了很好的補(bǔ)充。通出郏可以使用Java的動(dòng)態(tài)代理創(chuàng)建代理绢要,但當(dāng)要代理的類(lèi)沒(méi)有實(shí)現(xiàn)接口或者為了更好的性能,cglib是一個(gè)好的選擇拗小。
但是但是但是重罪,一個(gè)很致命的缺點(diǎn)是:cglib底層采用ASM字節(jié)碼生成框架,使用字節(jié)碼技術(shù)生成代理類(lèi),也就是生成的.class文件剿配,而我們?cè)赼ndroid中加載的是優(yōu)化后的.dex文件搅幅,也就是說(shuō)我們需要可以動(dòng)態(tài)生成.dex文件代理類(lèi),cglib在android中是不能使用的呼胚。但后面我們會(huì)根據(jù)dexmaker框架來(lái)仿照動(dòng)態(tài)生成.dex文件茄唐,實(shí)現(xiàn)cglib的動(dòng)態(tài)代理功能。
好了蝇更,我們先來(lái)看下cglib的強(qiáng)大吧~
舉個(gè)例子沪编,boss安排要實(shí)現(xiàn)一個(gè)人員管理的增刪改查功能,那這個(gè)簡(jiǎ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,搞定~但是呢蚁廓,需求是不斷變化的,過(guò)了幾天厨幻,boss又發(fā)話(huà)了相嵌,說(shuō)不是每個(gè)人都可以使用這個(gè)增刪改查功能的,要指定人員才可以使用克胳,那平绩。。我們就改吧~最直接的方式漠另,在每個(gè)方法上都加上判斷條件捏雌,但這么做多少有點(diǎn)太挫了,如果有五十個(gè)方法笆搓,那我們這么多方法就要都加一遍性湿,用cglib我們可以這么做:
final String name = "張si";
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(PeopleService.class);
//目標(biāo)對(duì)象攔截器,實(shí)現(xiàn)MethodInterceptor
//Object object為目標(biāo)對(duì)象
//Method method為目標(biāo)方法
//Object[] args 為參數(shù)满败,
//MethodProxy proxy CGlib方法代理對(duì)象
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("----對(duì)不起肤频,您沒(méi)有權(quán)限----");
}
return obj;
}
});
PeopleService ps = (PeopleService) enhancer.create();
ps.add();
只需添加一個(gè)攔截器,就可以攔截所有增刪改查操作算墨,從切面進(jìn)行統(tǒng)一管理宵荒,代碼量也不多。
又過(guò)了幾天净嘀,boss又發(fā)話(huà)了报咳,我們的增刪改查不是都要有限制,只有查找才對(duì)特定人員有限制挖藏,那我們就繼續(xù)改嘍~
這個(gè)時(shí)候我們就可以加入過(guò)濾器CallbackFilter:
final String name = "張武";
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(PeopleService.class);
//目標(biāo)對(duì)象攔截器暑刃,實(shí)現(xiàn)MethodInterceptor
//Object object為目標(biāo)對(duì)象
//Method method為目標(biāo)方法
//Object[] args 為參數(shù),
//MethodProxy proxy CGlib方法代理對(duì)象
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("----對(duì)不起膜眠,您沒(méi)有權(quán)限----");
}
return obj;
}
};
//NoOp.INSTANCE:這個(gè)NoOp表示no operator岩臣,即什么操作也不做溜嗜,代理類(lèi)直接調(diào)用被代理的方法不進(jìn)行攔截
enhancer.setCallbacks(new Callback[]{interceptor,NoOp.INSTANCE});
enhancer.setCallbackFilter(new CallbackFilter() {
//過(guò)濾方法
//返回的值為數(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();
我們沒(méi)有修改一行原有的增刪改查代碼炸宵,通過(guò)傳遞代理對(duì)象的方式輕松解決方法攔截、方法增強(qiáng)的業(yè)務(wù)需求狐树。
但遺憾的是焙压,cglib不支持android平臺(tái)。抑钟。涯曲。那我們就自己實(shí)現(xiàn)咯~在dexmaker和cglib-for-android庫(kù)的基礎(chǔ)上,修改部分代碼后形成我們的類(lèi)似cglib框架 MethodInterceptProxy 在塔,實(shí)現(xiàn)上面需求只需這樣寫(xiě)幻件,和cglib寫(xiě)法一致:
final String name = "張五";
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(PeopleService.class);
//目標(biāo)對(duì)象攔截器,實(shí)現(xiàn)MethodInterceptor
//Object object為目標(biāo)對(duì)象
//Method method為目標(biāo)方法
//Object[] args 為參數(shù)蛔溃,
//MethodProxy proxy CGlib方法代理對(duì)象
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("----對(duì)不起绰沥,您沒(méi)有權(quán)限----");
}
return obj;
}
};
//NoOp.INSTANCE:這個(gè)NoOp表示no operator,即什么操作也不做贺待,代理類(lèi)直接調(diào)用被代理的方法不進(jìn)行攔截
enhancer.setCallbacks(new MethodInterceptor[]{interceptor,NoOp.INSTANCE});
enhancer.setCallbackFilter(new CallbackFilter() {
//過(guò)濾方法
//返回的值為數(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