動態代理的作用
通過反射調用代理對象,讓其幫我們實現一些非常頻繁的操作,如:權限校驗和日志記錄
代理的實現原理:在Java中java.lang.reflect包下提供了一個Proxy類和一個InvocationHandler接口通過使用這個類和接口就可以生成動態代理對象。這里只能針對接口實現代理,后期cglib可以實現類的代理
代理的實現過程:既然是代理,顧名思義,就是代替某個類或接口去實現某個功能,也就是說,我代理了你,我就具備了你的功能。怎么才能具有其他類或接口的功能呢:要么是繼承自類,要么是實現接口。而我們已經知道,此處只能代理接口。所以在造該代理對象時,肯定是讓其實現了被代理的接口。而實現了接口還只是第一步,我還要提供一些特有的功能所以再創建完動態代理對象以后至少要實現這兩個目的:實現被代理的功能、并提供特殊功能。
具體步驟:首先由Proxy類的靜態方法newProxyInstance創建動態代理對象
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
而要創建該對象,需要3個參數ClassLoader loader:定義了由哪個ClassLoader對象來對生成的代理對象進行加載Class<?>[] interfaces:表示的是我將要給我需要代理的對象提供一組什么接口InvocationHandler h:既然一個指明了加載器,一個指明了接口,那么這個就是具體實現功能的方法了。InvocationHandler對象,表示的是當我這個動態代理對象在調用方法的時候,會關聯到哪一個InvocationHandler對象上每一個動態代理類都必須要實現InvocationHandler這個接口,當我們通過代理對象調用一個方法的時候,這個方法的調用就會被轉發為由InvocationHandler這個接口的invoke 方法來進行調用
public Object invoke(Object proxy, Method method, Object[] args)InvocationHandler
接口中invoke方法的三個參數:proxy:代表動態代理對象method:代表正在執行的方法args:代表調用目標方法時傳入的實參由此可知,實際在實現InvocationHandler接口重寫invoke方法時,所需要的三個參數都不要我們給出
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("注冊功能");
}
}
/*
* 由于該對象要重寫的方法中會調用代理對象的方法,所以需要把被代理的目標對象以參數的方法傳入
* 然后使用反射調用目標對象的方法
*/
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("權限校驗");
//通過反射執行目標對象的方法
method.invoke(target, args);
//代理要提供的特殊功能
System.out.println("日志記錄");
return null;//如果底層方法返回類型為 void,則該調用返回 null
}
}
創建動態代理對象并實現功能
import java.lang.reflect.Proxy;
/*
分析:
登錄注冊屬于用戶的擴展功能,用接口實現
然后重寫抽象方法,實現接口
創建實現InvocationHandler接口的對象作為Proxy的參數使用(其實,底層方法的調用即特殊功能的實現都是通過該對象實現的)
由Proxy的靜態方法創建動態代理對象,由于返回值是Object所以需要向下轉型
*/
public class ProxyDemo {
public static void main(String[] args) {
// 創建被代理對象
StudentDao st = new StudentDaoImpl();
// 創建實現InvocationHandler接口的對象
MyInvocationHandler mi = new MyInvocationHandler(st);
// 創建動態代理對象
StudentDao proxy = (StudentDao) Proxy.newProxyInstance(st.getClass().getClassLoader(), st.getClass().getInterfaces(), mi);
//用動態代理對象調用方法
proxy.login();
proxy.regist();
}
}