先看例子,demo 是一個增刪改查的數據庫操作,但我想在增加的這個操作上打一個日志,用來輸出,可以用靜態代理完成。
//接口
interface UserManager {
/**
* 增加
*
* @param userId
* @param userName
*/
void addUser(String userId, String userName);
/**
* 刪除
*
* @param userId
*/
void delUser(String userId);
/**
* 查找
*
* @param userId
* @return
*/
String findUser(String userId);
/**
* 修改
*
* @param userId
* @param userName
*/
void modifyUser(String userId, String userName);
}
//用戶管理實現類
public class UserManagerImpl implements UserManager {
@Override
public void addUser(String userId, String userName) {
System.out.println("UserManagerImpl.addUser");
}
@Override
public void delUser(String userId) {
System.out.println("UserManagerImpl.delUser");
}
@Override
public void modifyUser(String userId, String userName) {
System.out.println("UserManagerImpl.modifyUser");
}
@Override
public String findUser(String userId) {
System.out.println("UserManagerImpl.findUser");
return "張三";
}
}
//代理用戶管理實現類
public class UserManagerImplProxy implements UserManager {
// 目標對象
private UserManager userManager;
// 通過構造方法傳入目標對象
public UserManagerImplProxy(UserManager userManager) {
this.userManager = userManager;
}
@Override
public void addUser(String userId, String userName) {
try {
//開始添加用戶
System.out.println("start-->addUser()");
//執行添加的功能
userManager.addUser(userId, userName);
//添加用戶成功
System.out.println("success-->addUser()");
} catch (Exception e) {
//添加用戶失敗
System.out.println("error-->addUser()");
}
}
@Override
public void delUser(String userId) {
userManager.delUser(userId);
}
@Override
public String findUser(String userId) {
userManager.findUser(userId);
return "張三";
}
@Override
public void modifyUser(String userId, String userName) {
userManager.modifyUser(userId, userName);
}
}
//客戶端調用類
public class Client {
public static void main(String[] args) {
UserManager userManager = new UserManagerImplProxy(new UserManagerImpl());
userManager.addUser("1111", "張三");
}
執行結果:
start-->addUser()
UserManagerImpl.addUser
success-->addUser()
靜態代理類優缺點
優點:
代理使客戶端不需要知道實現類是什么,怎么做的,而客戶端只需知道代理即可(解耦合)。
缺點:
1)代理類和委托類實現了相同的接口,代理類通過委托類實現了相同的方法。這樣就出現了大量的代碼重復。如果接口增加一個方法,除了所有實現類需要實現這個方法外,所有代理類也需要實現此方法。增加了代碼維護的復雜度。
2)代理對象只服務于一種類型的對象,如果要服務多類型的對象。勢必要為每一種對象都進行代理,靜態代理在程序規模稍大時就無法勝任了。如上的代碼是只為UserManager類的訪問提供了代理,但是如果還要為其他類提供代理的話,就需要我們再次添加其它類的代理類。
動態代理:
代理類在程序運行前不存在、運行時由程序動態生成的代理方式稱為動態代理。
例子看studio暫未貼出
1.被代理對象targetObject通過參數傳遞進來
2.通過反射targetObject.getClass().getClassLoader()獲取ClassLoader對象
3.反射通過targetObject.getClass().getInterfaces()獲取它實現的所有接口
4.然后將targetObject包裝到實現了InvocationHandler接口的LogHandler對象中
5.最后通過newProxyInstance函數我們就獲得了一個動態代理對象。
在java的動態代理機制中,有兩個重要的類或接口,一個是 InvocationHandler(Interface)、另一個則是 Proxy(Class),這兩個類和接口是實現我們動態代理所必須用到的。
InvocationHandler類:
注釋
InvocationHandler is the interface implemented by the invocation handler of a proxy instance.
Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.
每一個動態代理類都必須要實現InvocationHandler這個接口,并且每個代理類的實例都關聯到了一個handler,當我們通過代理對象調用一個方法的時候,這個方法的調用就會被轉發為由InvocationHandler這個接口的 invoke 方法來進行調用。
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
proxy:
指代我們所代理的那個真實對象
method:
指代的是我們所要調用真實對象的某個方法的Method對象
args:
指代的是調用真實對象某個方法時接受的參數
Proxy類:
動態創建一個代理對象的類
public static Object newProxyInstance(ClassLoader var0, Class<?>[] var1, InvocationHandler var2) throws IllegalArgumentException
loader: 一個ClassLoader對象,定義了由哪個ClassLoader對象來對生成的代理對象進行加載
interfaces: 一個Interface對象的數組,表示的是我將要給我需要代理的對象提供一組什么接口,如果我提供了一組接口給它,那么這個代理對象就宣稱實現了該接口(多態),這樣我就能調用這組接口中的方法了
h: 一個InvocationHandler對象,表示的是當我這個動態代理對象在調用方法的時候,會關聯到哪一個InvocationHandler對象上
打印的日志:
invok調用開始
args = 1111
args = 張三
處理日志
UserManagerImpl.addUser
public abstract void com.example.DynamicProxy.UserManager.addUser(java.lang.String,java.lang.String)
invok調用成功
可以看到method打印出來的com.example.DynamicProxy.UserManager.addUser(java.lang.String,java.lang.String) 是符合newProxyInstance中第二個參數
targetObject.getClass().getInterfaces()
動態代理生成的都是二進制class字節碼:
Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)
1.InvocationHandler參數對象中加入了代理的接口類的實現類
2.通過Proxy.newProxyInstance產生$Proxy0類,它繼承Proxy對象,并根據第二個參數,實現了被代理類的所有接口
3.ClassLoader-代理類的類加載器,把當前的代理類加載到JVM中
動態代理生成的都是二進制class字節碼
優點:
可以方便對代理類的函數做統一或特殊處理,如記錄所有函數執行時間、所有函數執行前添加驗證判斷、對某個特殊函數進行特殊操作,而不用像靜態代理方式那樣需要修改每個函數。