動態(tài)代理的作用
通過反射調(diào)用代理對象廷蓉,讓其幫我們實現(xiàn)一些非常頻繁的操作剧罩,如:權限校驗和日志記錄
代理的實現(xiàn)原理:在Java中java.lang.reflect包下提供了一個Proxy類和一個InvocationHandler接口通過使用這個類和接口就可以生成動態(tài)代理對象籍救。這里只能針對接口實現(xiàn)代理渔嚷,后期cglib可以實現(xiàn)類的代理
代理的實現(xiàn)過程:既然是代理,顧名思義妻枕,就是代替某個類或接口去實現(xiàn)某個功能嚷往,也就是說葛账,我代理了你,我就具備了你的功能间影。怎么才能具有其他類或接口的功能呢:要么是繼承自類注竿,要么是實現(xiàn)接口茄茁。而我們已經(jīng)知道魂贬,此處只能代理接口。所以在造該代理對象時裙顽,肯定是讓其實現(xiàn)了被代理的接口付燥。而實現(xiàn)了接口還只是第一步,我還要提供一些特有的功能所以再創(chuàng)建完動態(tài)代理對象以后至少要實現(xiàn)這兩個目的:實現(xiàn)被代理的功能愈犹、并提供特殊功能键科。
具體步驟:首先由Proxy類的靜態(tài)方法newProxyInstance創(chuàng)建動態(tài)代理對象
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
而要創(chuàng)建該對象闻丑,需要3個參數(shù)ClassLoader loader:定義了由哪個ClassLoader對象來對生成的代理對象進行加載Class<?>[] interfaces:表示的是我將要給我需要代理的對象提供一組什么接口InvocationHandler h:既然一個指明了加載器,一個指明了接口勋颖,那么這個就是具體實現(xiàn)功能的方法了嗦嗡。InvocationHandler對象,表示的是當我這個動態(tài)代理對象在調(diào)用方法的時候饭玲,會關聯(lián)到哪一個InvocationHandler對象上每一個動態(tài)代理類都必須要實現(xiàn)InvocationHandler這個接口侥祭,當我們通過代理對象調(diào)用一個方法的時候,這個方法的調(diào)用就會被轉(zhuǎn)發(fā)為由InvocationHandler這個接口的invoke 方法來進行調(diào)用
public Object invoke(Object proxy, Method method, Object[] args)InvocationHandler
接口中invoke方法的三個參數(shù):proxy:代表動態(tài)代理對象method:代表正在執(zhí)行的方法args:代表調(diào)用目標方法時傳入的實參由此可知茄厘,實際在實現(xiàn)InvocationHandler接口重寫invoke方法時矮冬,所需要的三個參數(shù)都不要我們給出
public interface StudentDao {
public abstract void login();
public abstract void regist();
}
public class StudentDaoImpl implements StudentDao {
@Override
public void login() {
System.out.println("登錄功能");
}
@Override
public void regist() {
System.out.println("注冊功能");
}
}
/*
* 由于該對象要重寫的方法中會調(diào)用代理對象的方法,所以需要把被代理的目標對象以參數(shù)的方法傳入
* 然后使用反射調(diào)用目標對象的方法
*/
public class MyInvocationHandler implements InvocationHandler {
private Object target; //目標對象
public MyInvocationHandler(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//代理要提供的特殊功能
System.out.println("權限校驗");
//通過反射執(zhí)行目標對象的方法
method.invoke(target, args);
//代理要提供的特殊功能
System.out.println("日志記錄");
return null;//如果底層方法返回類型為 void次哈,則該調(diào)用返回 null
}
}
創(chuàng)建動態(tài)代理對象并實現(xiàn)功能
import java.lang.reflect.Proxy;
/*
分析:
登錄注冊屬于用戶的擴展功能胎署,用接口實現(xiàn)
然后重寫抽象方法,實現(xiàn)接口
創(chuàng)建實現(xiàn)InvocationHandler接口的對象作為Proxy的參數(shù)使用(其實窑滞,底層方法的調(diào)用即特殊功能的實現(xiàn)都是通過該對象實現(xiàn)的)
由Proxy的靜態(tài)方法創(chuàng)建動態(tài)代理對象琼牧,由于返回值是Object所以需要向下轉(zhuǎn)型
*/
public class ProxyDemo {
public static void main(String[] args) {
// 創(chuàng)建被代理對象
StudentDao st = new StudentDaoImpl();
// 創(chuàng)建實現(xiàn)InvocationHandler接口的對象
MyInvocationHandler mi = new MyInvocationHandler(st);
// 創(chuàng)建動態(tài)代理對象
StudentDao proxy = (StudentDao) Proxy.newProxyInstance(st.getClass().getClassLoader(), st.getClass().getInterfaces(), mi);
//用動態(tài)代理對象調(diào)用方法
proxy.login();
proxy.regist();
}
}