- 臨近秋招,備戰暑期實習,祝大家每天進步億點點!Day18
- 本篇總結的是 代理設計模式,后續會每日更新~

- 代理模式最直觀的解釋就是,通過代理,將被代理物件 “增強”!(即,擴展被代理物件的功能)
- 代理模式分為靜態代理,和動態代理:動態代理的代理類是動態生成的 , 靜態代理的代理類是我們提前寫好的邏輯,
- Java 中實作動態代理的方式有 2 種:
- JDK 動態代理
- CGLIB 動態代理
1、靜態代理
靜態代理角色分析:
- 抽象角色 :一般使用介面或者抽象類來實作,
- 真實角色 :被代理的角色,
- 代理角色: 代理真實角色 , 代理真實角色后 ,一般會做一些附屬的操作,
- 呼叫方:使用代理角色來進行一些操作,
我們以租客租客租房子為例,涉及到的物件有:租客、中介、房東,(房東即為被代理物件,中介即為代理物件)
租客通過中介之手租住房東的房子,代理物件中介需要尋找租客租房,并從中獲取中介費用,
代碼實作:
Rent.java 即抽象角色
// 抽象角色:租房
public interface Rent {
public void rent();
}
Host.java 即真實角色
// 真實角色: 房東,房東要出租房子
public class Host implements Rent{
public void rent() {
System.out.println("房屋出租");
}
}
Proxy.java 即代理角色
//代理角色:中介
public class Proxy implements Rent {
private Host host;
public Proxy() { }
public Proxy(Host host) {
this.host = host;
}
// 租房
public void rent(){
seeHouse();
host.rent();
fare();
}
// 看房
public void seeHouse(){
System.out.println("帶房客看房");
}
// 收中介費
public void fare(){
System.out.println("收中介費");
}
}
Client.java 呼叫方,即客戶
// 客戶類,一般客戶都會去找代理!
public class Client {
public static void main(String[] args) {
// 房東要租房
Host host = new Host();
// 中介幫助房東
Proxy proxy = new Proxy(host);
// 你去找中介!
proxy.rent();
}
}
靜態代理的缺點:
- 需要手動創建代理類,如果需要代理的物件多了,那么代理類也越來越多,
為了解決,這個問題,就有了動態代理 !
2、動態代理
說到動態代理,面試的時候肯定會問動態代理的兩種實作方式:
先來看公共的 UserService 介面,和 UserServiceImpl 實作類:
/**
* @author csp
* @date 2021-06-03
*/
public interface UserService {
/**
* 登錄
*/
void login();
/**
* 登出
*/
void logout();
}
/**
* @author csp
* @date 2021-06-03
*/
public class UserServiceImpl implements UserService{
@Override
public void login() {
System.out.println("用戶登錄...");
}
@Override
public void logout() {
System.out.println("用戶推出登錄...");
}
}
JDK 動態代理
代碼如下:
/**
* @author csp
* @date 2021-06-03
*/
public class JDKProxyFactory implements InvocationHandler {
// 目標物件(被代理物件)
private Object target;
public JDKProxyFactory(Object target) {
super();
this.target = target;
}
/**
* 創建代理物件
*
* @return
*/
public Object createProxy() {
// 1.得到目標物件的類加載器
ClassLoader classLoader = target.getClass().getClassLoader();
// 2.得到目標物件的實作介面
Class<?>[] interfaces = target.getClass().getInterfaces();
// 3.第三個引數需要一個實作invocationHandler介面的物件
Object newProxyInstance = Proxy.newProxyInstance(classLoader, interfaces, this);
return newProxyInstance;
}
/**
* 真正執行代理增強的方法
*
* @param proxy 代理物件.一般不使用
* @param method 需要增強的方法
* @param args 方法中的引數
* @return
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("JDK 動態代理:登錄/登出前邏輯校驗......");
Object invoke = method.invoke(target, args);
System.out.println("JDK 動態代理:登錄/登出后日志列印......");
return invoke;
}
public static void main(String[] args) {
// 1.創建物件
UserServiceImpl userService = new UserServiceImpl();
// 2.創建代理物件
JDKProxyFactory jdkProxyFactory = new JDKProxyFactory(userService);
// 3.呼叫代理物件的增強方法,得到增強后的物件
UserService userServiceProxy = (UserService) jdkProxyFactory.createProxy();
userServiceProxy.login();
System.out.println("==================================");
userServiceProxy.logout();
}
}
輸出結果如下:
JDK 動態代理:登錄/登出前邏輯校驗......
用戶登錄...
JDK 動態代理:登錄/登出后日志列印......
==================================
JDK 動態代理:登錄/登出前邏輯校驗......
用戶推出登錄...
JDK 動態代理:登錄/登出后日志列印......
CGLIB 動態代理
代碼如下:
/**
* @author csp
* @date 2021-06-03
*/
public class CglibProxyFactory implements MethodInterceptor {
// 目標物件(被代理物件)
private Object target;
// 使用構造方法傳遞目標物件
public CglibProxyFactory(Object target) {
super();
this.target = target;
}
/**
* 創建代理物件
*
* @return
*/
public Object createProxy() {
// 1.創建Enhancer
Enhancer enhancer = new Enhancer();
// 2.傳遞目標物件的class
enhancer.setSuperclass(target.getClass());
// 3.設定回呼操作
enhancer.setCallback(this);
return enhancer.create();
}
/**
* 真正執行代理增強的方法
* @param o 代理物件
* @param method 要增強的方法
* @param objects 要增強方法的引數
* @param methodProxy 要增強的方法的代理
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("cglib 動態代理:登錄/登出前邏輯校驗......");
Object invoke = method.invoke(target, objects);
System.out.println("cglib 動態代理:登錄/登出后日志列印......");
return invoke;
}
public static void main(String[] args) {
// 1.創建物件
UserServiceImpl userService = new UserServiceImpl();
// 2.創建代理物件
CglibProxyFactory cglibProxyFactory = new CglibProxyFactory(userService);
// 3.呼叫代理物件的增強方法,得到增強后的物件
UserService userServiceProxy = (UserService) cglibProxyFactory.createProxy();
userServiceProxy.login();
System.out.println("==================================");
userServiceProxy.logout();
}
}
測驗結果如下:
cglib 動態代理:登錄/登出前邏輯校驗......
用戶登錄...
cglib 動態代理:登錄/登出后日志列印......
==================================
cglib 動態代理:登錄/登出前邏輯校驗......
用戶推出登錄...
cglib 動態代理:登錄/登出后日志列印......
3、總結
面試題一:JDK動態代理和CGLIB動態代理區別?
- ① JDK 動態代理本質上是實作了被代理物件的介面,而 CGLib 本質上是繼承了被代理物件,覆寫其中的方法,
- ② JDK 動態代理只能對實作了介面的類生成代理,CGLib 則沒有這個限制,但是 CGLib 因為使用繼承實作,所以 CGLib 所以無法對
final類、private方法和static方法進行代理, - ③ JDK 動態代理是 JDK 里自帶的,CGLib 動態代理需要引入第三方的
jar包, - ④ 在呼叫代理方法上,JDK動態代理是通過反射機制呼叫,CGLib 是通過 FastClass 機制直接呼叫,(看過一篇文章,介紹說 FastClass 簡單的理解,就是使用一個 index 下標作為入參,可以直接定位到要呼叫的方法直接,并進行呼叫)
在性能上,JDK1.7 之前,由于使用了 FastClass 機制,CGLib 在執行效率上比 JDK 快,但是隨著 JDK 動態代理的不斷優化,從 JDK 1.7 開始,JDK 動態代理已經明顯比 CGLib 更快了,
面試題一:JDK 動態代理為什么只能對實作了介面的類生成代理?
根本原因是通過 JDK 動態代理生成的類已經繼承了 Proxy 類,所以無法再使用繼承的方式去對類實作代理,
總結的面試題也挺費時間的,文章會不定時更新,有時候一天多更新幾篇,如果幫助您復習鞏固了知識點,還請三連支持一下,后續會億點點的更新!

為了幫助更多小白從零進階 Java 工程師,從CSDN官方那邊搞來了一套 《Java 工程師學習成長知識圖譜》,尺寸 870mm x 560mm,展開后有一張辦公桌大小,也可以折疊成一本書的尺寸,有興趣的小伙伴可以了解一下,當然,不管怎樣博主的文章一直都是免費的~

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/286378.html
標籤:java
