淺談代理模式
2022年04月06日 第一版
2023年12月13日 第二版本更新,學而時習,思則常新
1. 代理模式
拋開精密的學術用語,就是在不修改原來對象的前提下,提供額外的功能與操作。
2. 分類

下面先展示幾個類,JDK 、CGLIB 共同會用:一個接口+ 一個實現類
/**
* @author: handsometaoa
* @description
* @date: 2023/12/13 11:43
*/
public interface UserService {
String showMsg();
}
/**
* @author: handsometaoa
* @description
* @date: 2023/12/13 11:44
*/
@Service
public class UserServiceImpl implements UserService {
@Override
public String showMsg() {
System.out.println("展示消息");
return "返回信息";
}
}
2.1 靜態代理:
現在我有一個功能,想要在方法A前后加日志、加控制權限、加審計,但是我不想修改A方法。
雖然你只添加了日志,出事都找你??
那就可以把 【日志+被代理對象.方法(舊方法)+日志】作為新方法。
代理類:
public class UserServiceLogProxy implements UserService {
private final UserService userService;
public UserServiceLogProxy(UserService userService) {
this.userService = userService;
}
@Override
public void sayHello() {
System.out.println("開始記錄日志");
userService.showMsg();
System.out.println("結束記錄日志");
}
}
缺點:
比如說,我現在有100個類似的方法,都需要加日志,我需要寫100個代理類。
按道理(開始講道理了)日志信息是功能相同的,可以復用。
2.2 動態代理
我們設想可以把日志方法抽象出去,動態插入各個想記錄日志的方法,熱插拔(這個詞妙,生動形象),美哉。

2.2.1 JDK 動態代理
實現步驟:
- 定義一個接口及其實現類
- 實現
InvocationHandler(java.lang.reflect.InvocationHandler) 并重寫invoke方法,在invoke方法中我們會調用原生方法(被代理類方法)并增加自定義邏輯(日志等功能) - 通過
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler)方法創建代理對象JDK代理有一個缺點就是只能代理實現接口的類
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler);
代碼:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author: handsometaoa
* @description JDK動態代理,基于接口
* @date: 2023/12/13 11:55
*/
public class LogProxyJdk implements InvocationHandler {
private final Object target;
public LogProxyJdk(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("開始記錄日志");
Object result = method.invoke(target, args);
System.out.println("結束記錄日志");
return result;
}
/**
* 獲取代理對象
*/
public Object getProxy() {
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
}
使用:
@RestController
@RequestMapping("dynamic")
public class DynamicController {
@Resource
private UserService userService;
@RequestMapping("/jdk")
String jdk() {
LogProxyJdk logProxyJdk = new LogProxyJdk(userService);
UserService proxy = (UserService) logProxyJdk.getProxy();
return proxy.showMsg();
}
}
缺點:
JDK動態代理只能代理有接口的方法,如果被代理類未實現接口,我們可以用 CGLIB 動態代理機制來實現。
2.2.2 CGLIB 動態代理
實現步驟:
- 定義一個類
- 實現
MethodInterceptor(org.springframework.cglib.proxy.MethodInterceptor) 并重寫intercept方法,intercept用于攔截增強被代理類的方法 - 通過 Enhancer 類的
create()方法創建代理類 ,設置父類及回調類,檢測到攔截方法時會調用 intercept 方法。
CGLIB 通過fast-class來提高效率,首先將方法都建立索引,然后通過索引快速查找,來調用方法
代碼:
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @author: handsometaoa
* @description CGLIB 動態代理
* @date: 2023/12/13 11:43
*/
public class LogProxyCglib implements MethodInterceptor {
private final Class clazz;
public LogProxyCglib(Class clazz) {
this.clazz = clazz;
}
public Object getProxy() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz); // 設置父類
enhancer.setCallback(this); // 設置回調,設置為 this,當調用攔截方法時,會調用攔截器的 intercept 方法
return enhancer.create();
}
/**
* @param object 表示要攔截的對象,即被代理的對象。
* @param method 表示要攔截的方法,即被代理對象的方法。
* @param args 表示方法的參數列表,即被代理對象方法的參數。
* @param methodProxy 表示用于調用原始方法的代理對象。
*/
@Override
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("開始記錄日志");
Object result = methodProxy.invokeSuper(object, args);
System.out.println("結束記錄日志");
return result;
}
}
使用
@RestController
@RequestMapping("dynamic")
public class DynamicController {
@RequestMapping("/cglib")
String cglib(){
LogProxyCglib logProxyCglib = new LogProxyCglib(UserServiceImpl.class);
UserService proxy = (UserService) logProxyCglib.getProxy();
return proxy.showMsg();
}
}
3. 參考文獻 站在前人的腳上,踏步前進。
本文來自博客園,作者:帥氣的濤啊,轉載請注明原文鏈接:http://www.rzrgm.cn/handsometaoa/p/16107991.html

浙公網安備 33010602011771號