一、動態代理
-
動態代理和靜態代理角色都一樣(抽象角色、真實角色、代理角色)。
-
動態代理的代理類是動態生成的,不是我們直接寫好的。(靜態代理是我們自己寫了代理類,但是動態代理就不需要我們自己的寫代理類了。)
-
動態代理分爲:
- 基於接口的:JDK 動態代理
- 基於類:cglib
- 基於Java 字節碼:Javassist
我們主要學習JDK 的動態代理(Spring AOP默認使用JDK動態代理)。
需要在JDK的API文檔中去看看 InvocationHandler(接口)、Proxy(類) 。(在反射包reflect下)
二、租房案例
還是使用上一篇靜態代理中的案例,不過這次使用的是動態代理!
對象:房東(Renter )、中介(Proxy)
出租事件:Rent接口的rent()方法,用於出租。
關係:中介(代理對象)可以代理房東(真實對象)租房子。
1.抽象角色
抽象角色:代理角色(代理人)和真實角色可以實現)
這裏抽象角色爲一個接口:出租方法,用於出租房屋。
/** 房東Host和代理人中介Proxy都可以實現出租Rent這件事情(方法) */
public interface Rent {
/** 出租房屋 */
void rent();
}
2.真實角色
真實角色:真實的角色,可以實現業務方法.
真實角色房東,可以實現出租房屋。房東僅僅只用關注出租房屋。
/** 房東:可以出租房屋的人 */
public class Renter implements Rent {
@Override
public void rent() {
System.out.println("房東出租房子。");
}
}
3.創建調用處理器
創建一個類並且實現InvocationHandler接口。
用這個類來生成代理實例。
/** 調用處理類實例:(每個代理實例都有一個關聯的調用處理程序) */
public class MyInvocationHandler implements InvocationHandler {
/** 被代理的接口 */
private Rent rent;
public void setRent(Rent rent) {
this.rent = rent;
}
/** 真正生成代理類的方法:返回rent接口的代理類實例 */
public Object getProxy() {
// 參數解釋:loader:類加載器來定義代理類。interfaces :代理類實現的接口列表。h:調度方法調用的調用處理函數(因爲實現了InvocationHandler所以直接填this)
return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(), this);
}
/**
* 處理代理實例,並返回結果。(處理代理實例上的方法調用並返回結果。)
* proxy:調用該方法的代理實例(代理類代理的真實代理對象com.sun.proxy.$Proxy0(生成的代理對象)
* method:我們所要調用某個對象真實的方法的Method對象
* args:指代代理對象方法傳遞的參數
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
// 方法調用
Object result = method.invoke(rent, args);
after();
return result;
}
void before() {
System.out.println("----- 中介帶你看房 -----");
}
void after() {
System.out.println("----- 中介帶你籤合同 -----");
}
}
4.測試
創建調用處理器,set置入需要代理的對象(房東),獲取代理對象(中介)。最後代理對象(中介)執行出租方法。
/** 租客:需要租房子的人 */
public class Tenant {
/** 顧客去租房 */
public static void main(String[] args) {
// 創建調用處理器
MyInvocationHandler ih = new MyInvocationHandler();
// set 置入需要代理的對象(房東)
ih.setRent(new Renter());
// 獲取代理對象(中介)
Rent proxy = (Rent) ih.getProxy();
// 代理對象,執行出租方法。
proxy.rent();
}
}
輸出:
----- 中介帶你看房 -----
房東出租房子。
----- 中介帶你籤合同 -----
三、真實業務案例
還是使用上一篇靜態代理中的案例,不過這次使用的是動態代理!
需求:
增加業務日誌輸出功能:業務調用前需要輸出調用的方法名,調用後需要輸出返回值。
1.抽象角色
用戶增刪改查接口。
/** 用戶數據訪問層接口 */
public interface UserDao {
void ins();
void del();
void upd();
void sel();
}
2.真實對象
用戶接口的實現類
public class UserDaoImpl implements UserDao {
@Override
public void ins() {
System.out.println("insert命令");
}
@Override
public void del() {
System.out.println("delete命令");
}
@Override
public void upd() {
System.out.println("update命令");
}
@Override
public void sel() {
System.out.println("select命令");
}
}
3.創建調用處理類
創建調用處理類
/** 調用處理類實例:(每個代理實例都有一個關聯的調用處理程序) */
public class MyInvocationHandler implements InvocationHandler {
private UserDao userDao;
public void setUserDaoImpl(UserDao userDao) {
this.userDao = userDao;
}
/** 獲取代理對象 */
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(), userDao.getClass().getInterfaces(), this);
}
/** 處理代理實例,並返回結果。 */
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method.getName());
Object result = method.invoke(userDao, args);
logReturnType(method.getReturnType().getName());
return result;
}
/** 日誌 */
private void log(String name) {
System.out.println("[info]: ----- "+name + "()方法 -----");
}
/** 日誌 */
private void logReturnType(String name) {
System.out.println("[info]: ----- 返回類型:"+name + " -----");
}
}
4.測試
創建調用處理類實例,set注入需要代理的對象(UserDaoImpl),獲取代理對象,最後執行代理對象的插入方法
public class Test {
public static void main(String[] args) {
// 創建調用處理類實例
MyInvocationHandler ih = new MyInvocationHandler();
// set 置入需要代理的對象(UserDaoImpl)
ih.setUserDaoImpl(new UserDaoImpl());
// 獲取代理對象(UserDaoImpl)
UserDao proxy = (UserDao) ih.getProxy();
// 代理對象,執行插入方法。
proxy.ins();
}
}
輸出:
[info]: ----- ins()方法 -----
insert命令
[info]: ----- 返回類型:void -----
四、封裝成通用類
將剛剛使用的兩個MyInvocationHandler給封裝成通用類。至此i,JDK的動態代理已結束,開始學習Spring 的AOP。
/** 調用處理類實例:(每個代理實例都有一個關聯的調用處理程序) */
public class MyInvocationHandler implements InvocationHandler {
/** 目標:被代理的接口 */
private Object target;
public void setTarget(Object target) {
this.target = target;
}
/** 返回代理類實例 */
public Object getProxy() {
// 參數解釋:loader:類加載器來定義代理類。interfaces :代理類實現的接口列表。h:調度方法調用的調用處理函數(因爲實現了InvocationHandler所以直接填this)
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
/** 處理代理實例,並返回結果。(處理代理實例上的方法調用並返回結果。) */
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 方法調用
Object result = method.invoke(target, args);
return result;
}
}
相關
我的該分類的其他相關文章,請點擊:【Spring + Spring MVC + MyBatis】文章目錄