Java代理的幾種實(shí)現(xiàn)方式總結(jié)
1. 簡(jiǎn)介
本文將通過例子說明java代理的幾種實(shí)現(xiàn)方式,并比較它們之間的差異。
1.1 例子應(yīng)用場(chǎng)景
代理對(duì)象給被代理對(duì)象(目標(biāo)對(duì)象),增加一個(gè)目標(biāo)對(duì)象方法被調(diào)用的日志,用于后續(xù)通過日志采集統(tǒng)計(jì)方法被調(diào)用次數(shù)。
1.2 被代理對(duì)象代碼
后面所有例子均使用這些相同代碼。
● 被代理對(duì)象接口
public interface Animal {
void say();
}
● 被代理對(duì)象Dog類
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class Dog implements Animal {
@Override
public void say() {
log.info("汪汪");
}
}
2. 靜態(tài)代理
簡(jiǎn)單點(diǎn)說,靜態(tài)代理就是在編碼階段已經(jīng)確定了代理對(duì)象。
這里簡(jiǎn)單回顧下代理模式的基本類結(jié)構(gòu):

| 類 | 描述 |
|---|---|
| Subject | 被代理目標(biāo)類的接口。 |
| RealSubject | 被代理目標(biāo)類。 |
| ProxySubject | 代理類,需要實(shí)現(xiàn)被代理目標(biāo)類接口,同時(shí)持有被代理目標(biāo)類對(duì)象實(shí)例,實(shí)際調(diào)用時(shí),通過委托的方式調(diào)用被代理目標(biāo)類。 |
| SubjectFactory | 被代理目標(biāo)類的工廠類,通過工廠類屏蔽創(chuàng)建Subject的細(xì)節(jié),客戶端看到的是Subject,至于具體是哪個(gè)實(shí)現(xiàn)類不需要關(guān)心。 |
2.1 代碼示例
2.1.1 DogStaticProxy
Dog代理類:
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@AllArgsConstructor
// 代理類,實(shí)現(xiàn)Subject接口
public class DogStaticProxy implements Animal {
// 持有RealSubject對(duì)象實(shí)例
private Dog dog;
@Override
public void say() {
// 增強(qiáng)的能力,打印方法調(diào)用日志
log.info("Dog.say() called.");
// 通過委托的方式調(diào)用RealSubject對(duì)象實(shí)例方法
dog.say();
}
}
2.1.2 DuckStaticProxy
Duck代理類:
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@AllArgsConstructor
public class DuckStaticProxy implements Animal {
private Duck duck;
@Override
public void say() {
log.info("Dog.say() called.");
duck.say();
}
}
2.1.3 StaticProxyFactory
代理工廠類:
public class StaticProxyFactory {
// 屏蔽創(chuàng)建Subject實(shí)例的細(xì)節(jié),使用者看到的只有Subject接口,即Animal
public static Animal createDog() {
return new DogStaticProxy(new Dog());
}
public static Animal createDuck() {
return new DuckStaticProxy(new Duck());
}
}
2.1.4 StaticProxyFactoryTest
測(cè)試代碼:
import org.junit.jupiter.api.Test;
class StaticProxyFactoryTest {
@Test
public void exec() {
Animal dog = StaticProxyFactory.createDog();
Animal duck = StaticProxyFactory.createDuck();
dog.say();
duck.say();
}
}
2.1.5 輸出日志
2023-12-28 12:00:35 INFO DogStaticProxy:16 - Dog.say() called.
2023-12-28 12:00:35 INFO Dog:10 - 汪汪
2023-12-28 12:00:35 INFO DuckStaticProxy:13 - Dog.say() called.
2023-12-28 12:00:35 INFO Duck:9 - 嘎嘎
3. JDK動(dòng)態(tài)代理
動(dòng)態(tài)代理就是在編碼階段還沒有確定代理對(duì)象,在運(yùn)行期才動(dòng)態(tài)確定。
如下是JDK動(dòng)態(tài)代理的類結(jié)構(gòu):

| 類 | 描述 |
|---|---|
| Subject | 被代理目標(biāo)類的接口。 |
| RealSubject | 被代理目標(biāo)類。 |
| InvocationHandler | 可以理解為一個(gè)攔截器,被代理對(duì)象方法調(diào)用時(shí)都會(huì)先調(diào)用此接口的invoke方法,在invoke方法內(nèi)部去調(diào)用被代理對(duì)象方法。 |
| InvocationHandler實(shí)現(xiàn)類 | InvocationHandler的實(shí)現(xiàn)類 |
| Proxy | JDK代理工具類,用于生成代理對(duì)象,生成代理對(duì)象時(shí)會(huì)用到RealSubject和InvocationHandler實(shí)現(xiàn)類。 |
3.1 代碼示例
3.1.1 AnimalInvocationHandler
InvocationHandler的實(shí)現(xiàn)類:
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
@Slf4j
public class AnimalInvocationHandler implements InvocationHandler {
private Animal animal;
// 通過構(gòu)造器傳入目標(biāo)類實(shí)例對(duì)象
public AnimalInvocationHandler(Animal animal) {
this.animal = animal;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 增強(qiáng)能力,打印方法調(diào)用日志,采集后統(tǒng)計(jì)調(diào)用次數(shù)
log.info(getCalledMethodInfo(method));
// 調(diào)用目標(biāo)類方法,參數(shù)是目標(biāo)類實(shí)例和對(duì)應(yīng)的方法參數(shù)
return method.invoke(animal, args);
}
// 輔助方法,返回被調(diào)用方法信息
private String getCalledMethodInfo(Method method) {
StringBuilder builder = new StringBuilder();
builder.append(animal.getClass().getSimpleName())
.append(".")
.append(method.getName())
.append("() called.");
return builder.toString();
}
}
3.1.2 JDKProxyFactory
代理工廠類:
import java.lang.reflect.Proxy;
public class JDKProxyFactory {
public static Animal createDog() {
return getProxy(new Dog());
}
public static Animal createDuck() {
return getProxy(new Duck());
}
private static Animal getProxy(Animal implInstance) {
AnimalInvocationHandler handler = new AnimalInvocationHandler(implInstance);
// 創(chuàng)建代理對(duì)象
Object proxy = Proxy.newProxyInstance(implInstance.getClass().getClassLoader(),
implInstance.getClass().getInterfaces(),
handler);
return (Animal) proxy;
}
}
3.1.3 JDKProxyFactoryTest
測(cè)試類:
import org.junit.jupiter.api.Test;
class JDKProxyFactoryTest {
@Test
public void exec() {
Animal dog = JDKProxyFactory.createDog();
Animal duck = JDKProxyFactory.createDuck();
dog.say();
duck.say();
}
}
3.1.4 輸出日志
2023-12-28 12:27:37 INFO AnimalInvocationHandler:19 - Dog.say() called.
2023-12-28 12:27:37 INFO Dog:10 - 汪汪
2023-12-28 12:27:37 INFO AnimalInvocationHandler:19 - Duck.say() called.
2023-12-28 12:27:37 INFO Duck:9 - 嘎嘎
3.2 通用能力重構(gòu)
打印方法調(diào)用日志不僅僅適用于Animal,也適用于其他所有有此需求的類,可以優(yōu)化一下。
注:此優(yōu)化非JDK動(dòng)態(tài)代理的能力,存粹就是順便處理一下,重構(gòu)無處不在,只要你想做。
3.2.1 代碼
3.2.1.1 MethodPrinterInvocationHandler
AnimalInvocationHandler只能適用于Animal,重構(gòu)通過泛型實(shí)現(xiàn):
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
@Slf4j
// 使用泛型,只要是有接口的實(shí)現(xiàn)類都可以使用
public class MethodPrinterInvocationHandler<T> implements InvocationHandler {
private T t;
public MethodPrinterInvocationHandler(T t) {
this.t = t;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log.info(getCalledMethod(method));
return method.invoke(t, args);
}
private String getCalledMethod(Method method) {
StringBuilder builder = new StringBuilder();
builder.append(t.getClass().getSimpleName())
.append(".")
.append(method.getName())
.append("() called.");
return builder.toString();
}
}
3.2.1.2 MethodPrinterJDKProxyFactory
代理工廠類同樣通過泛型實(shí)現(xiàn):
import java.lang.reflect.Proxy;
public class MethodPrinterJDKProxyFactory {
// 使用泛型,除了Animal接口和其子類,其他接口和子類也可以使用
public static <T, R> R getProxy(T implInstance ) {
MethodPrinterInvocationHandler<T> handler = new MethodPrinterInvocationHandler(implInstance);
return (R) Proxy.newProxyInstance(implInstance.getClass().getClassLoader(),
implInstance.getClass().getInterfaces(),
handler);
}
}
3.2.1.3 AnimalJDKProxyFactory
public class AnimalJDKProxyFactory {
public static Animal createDuck() {
return MethodPrinterJDKProxyFactory.getProxy(new Duck());
}
public static Animal createDog() {
return MethodPrinterJDKProxyFactory.getProxy(new Dog());
}
}
3.2.1.4 MethodPrinterJDKProxyFactoryTest
import org.junit.jupiter.api.Test;
class MethodPrinterJDKProxyFactoryTest {
@Test
public void exec() {
Animal dog = AnimalJDKProxyFactory.createDog();
Animal duck = AnimalJDKProxyFactory.createDuck();
dog.say();
duck.say();
}
}
3.2.1.5 打印日志
2023-12-28 12:54:00 INFO MethodPrinterInvocationHandler:18 - Dog.say() called.
2023-12-28 12:54:00 INFO Dog:10 - 汪汪
2023-12-28 12:54:00 INFO MethodPrinterInvocationHandler:18 - Duck.say() called.
2023-12-28 12:54:00 INFO Duck:9 - 嘎嘎
3.3 JDK動(dòng)態(tài)代理限制
JDK動(dòng)態(tài)代理只能代理接口,無法直接代理類,下面看個(gè)例子。
3.3.1 代碼
3.3.1.1 Cat
新增一個(gè)類,沒有實(shí)現(xiàn)Animal接口,用于證明沒有接口無法使用JDK動(dòng)態(tài)代理:
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class Cat {
public void say() {
log.info("喵喵");
}
}
3.3.1.2 CatInvocationHandler
攔截器也要調(diào)整,將Cat作為構(gòu)造器參數(shù),不能再使用Animal:
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
@Slf4j
public class CatInvocationHandler implements InvocationHandler {
private Cat cat;
public CatInvocationHandler(Cat cat) {
this.cat = cat;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log.info(getCalledMethod(method));
return method.invoke(cat, args);
}
private String getCalledMethod(Method method) {
StringBuilder builder = new StringBuilder();
builder.append(cat.getClass().getSimpleName())
.append(".")
.append(method.getName())
.append("() called.");
return builder.toString();
}
}
3.3.1.3 ErrorJDKProxyFactory
代理工廠類:
import java.lang.reflect.Proxy;
public class ErrorJDKProxyFactory {
public static Cat createCat() {
return getProxy(new Cat());
}
private static Cat getProxy(Cat cat) {
CatInvocationHandler handler = new CatInvocationHandler(cat);
// 這里的寫法仍然不變,和之前一樣,只是類型變化
return (Cat) Proxy.newProxyInstance(cat.getClass().getClassLoader(), cat.getClass().getInterfaces(), handler);
}
}
3.3.1.4 ErrorJDKProxyFactoryTest
測(cè)試類:
import org.junit.jupiter.api.Test;
class ErrorJDKProxyFactoryTest {
@Test
void createCat() {
Cat cat = ErrorJDKProxyFactory.createCat();
cat.say();
}
}
3.3.1.5 輸出日志
java.lang.ClassCastException: com.sun.proxy.$Proxy8 cannot be cast to com.kengcoder.javaawsome.proxy.jdkproxy.Cat at com.kengcoder.javaawsome.proxy.jdkproxy.ErrorJDKProxyFactory.getProxy(ErrorJDKProxyFactory.java:13) at com.kengcoder.javaawsome.proxy.jdkproxy.ErrorJDKProxyFactory.createCat(ErrorJDKProxyFactory.java:8)
3.3.1.6 原因分析
● 通過類代理
兩個(gè)對(duì)象類型不一致,沒法做強(qiáng)轉(zhuǎn)。
● 通過接口代理

代理對(duì)象類型為Animal,可以強(qiáng)轉(zhuǎn)為Animal接口。
4. CGLIB動(dòng)態(tài)代理
CGLIB動(dòng)態(tài)代理是三方庫(kù)實(shí)現(xiàn),和JDK動(dòng)態(tài)代理的區(qū)別是:既可以代理接口,也可以代理類。
CGLIB動(dòng)態(tài)代理實(shí)現(xiàn)的類結(jié)構(gòu)如下:

| 類 | 描述 |
|---|---|
| Subject | 被代理目標(biāo)類的接口。 |
| RealSubject | 被代理目標(biāo)類。 |
| MethodInterceptor | 可以理解為一個(gè)攔截器,被代理對(duì)象方法調(diào)用時(shí)都會(huì)先調(diào)用此接口的intercept方法,在intercept方法內(nèi)部去調(diào)用被代理對(duì)象方法。 |
| MethodInterceptor實(shí)現(xiàn)類 | MethodInterceptor的實(shí)現(xiàn)類 |
| Enhancer | 代理工具類,用于生成代理對(duì)象,生成代理對(duì)象時(shí)會(huì)用到RealSubject和MethodInterceptor實(shí)現(xiàn)類。 |
4.1 代碼
4.1.1 CglibInterceptor
MethodInterceptor的實(shí)現(xiàn)類:
import lombok.extern.slf4j.Slf4j;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
@Slf4j
public class CglibInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
log.info(getCalledMethod(method));
return methodProxy.invokeSuper(target, args);
}
private String getCalledMethod(Method method) {
StringBuilder builder = new StringBuilder();
builder.append(method.getDeclaringClass().getSimpleName())
.append(".")
.append(method.getName())
.append("() called.");
return builder.toString();
}
}
4.1.2 CglibProxyFactory
CGLIB代理工廠類,抽象出通用方法:
import net.sf.cglib.proxy.Enhancer;
public class CglibProxyFactory {
public static <T> T getProxy(Class<T> clazz) {
Enhancer enhancer = new Enhancer();
// 設(shè)置超類,字面看就是代理對(duì)象是目標(biāo)類的子類,通過繼承方式實(shí)現(xiàn),而不是組合方式實(shí)現(xiàn)。
enhancer.setSuperclass(clazz);
enhancer.setCallback(new CglibInterceptor());
return (T)enhancer.create();
}
}
4.1.3 AnimalCglibProxyFactory
Animal工廠類:
public class AnimalCglibProxyFactory {
public static Animal createDog() {
return CglibProxyFactory.getProxy(Dog.class);
}
public static Animal createDuck() {
return CglibProxyFactory.getProxy(Duck.class);
}
}
4.1.4 CglibProxyFactoryTest
import org.junit.jupiter.api.Test;
class CglibProxyFactoryTest {
@Test
public void exec() {
Animal dog = AnimalCglibProxyFactory.createDog();
Animal duck = AnimalCglibProxyFactory.createDuck();
dog.say();
duck.say();
}
}
4.1.5 輸出日志
2023-12-28 13:23:56 INFO CglibInterceptor:13 - Dog.say() called.
2023-12-28 13:23:56 INFO Dog:10 - 汪汪
2023-12-28 13:23:56 INFO CglibInterceptor:13 - Duck.say() called.
2023-12-28 13:23:56 INFO Duck:9 - 嘎嘎
5. 總結(jié)
本文介紹了幾種java代理的實(shí)現(xiàn)方式,在過程中通過重構(gòu)展示了如何通過java泛型實(shí)現(xiàn)通用能力,以及通過工廠模式屏蔽類實(shí)例創(chuàng)建細(xì)節(jié),提升擴(kuò)展性。
幾種java代理的差別如下:
| 比較項(xiàng) | 靜態(tài)代理 | JDK動(dòng)態(tài)代理 | CGLIB動(dòng)態(tài)代理 |
|---|---|---|---|
| 代理對(duì)象 | 接口和類 | 接口 | 接口和類 |
| 實(shí)現(xiàn)接口方法 | 需手動(dòng)一一實(shí)現(xiàn) | 自動(dòng)實(shí)現(xiàn) | 自動(dòng)實(shí)現(xiàn) |
| 攔截方法 | 每個(gè)方法獨(dú)立實(shí)現(xiàn),完全隔離開。 | 都在一處實(shí)現(xiàn),當(dāng)指定方法攔截時(shí)要增加判斷邏輯。 | 同靜態(tài)代理。 |
6. 最后
以上就是Java代理的幾種實(shí)現(xiàn)方式總結(jié)的詳細(xì)內(nèi)容,更多關(guān)于Java代理實(shí)現(xiàn)方式的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java利用布隆過濾器實(shí)現(xiàn)快速檢查元素是否存在
布隆過濾器是一個(gè)很長(zhǎng)的二進(jìn)制向量和一系列隨機(jī)映射函數(shù)。布隆過濾器可以用于檢索一個(gè)元素是否在一個(gè)集合中。本文就來詳細(xì)說說實(shí)現(xiàn)的方法,需要的可以參考一下2022-10-10
springboot實(shí)現(xiàn)配置本地訪問端口及路徑
這篇文章主要介紹了springboot實(shí)現(xiàn)配置本地訪問端口及路徑,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01
一文教你學(xué)會(huì)如何使用MyBatisPlus
本篇文章,我們通過 MyBatis Plus 來對(duì)一張表進(jìn)行 CRUD 操作,來看看是如何簡(jiǎn)化我們開發(fā)的,文中通過代碼示例介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-05-05
Java下載遠(yuǎn)程服務(wù)器文件到本地(基于http協(xié)議和ssh2協(xié)議)
這篇文章主要介紹了Java下載遠(yuǎn)程服務(wù)器文件到本地的方法(基于http協(xié)議和ssh2協(xié)議),幫助大家更好的理解和使用Java,感興趣的朋友可以了解下2021-01-01
SpringBoot整合Minio的過程(支持公有及私有bucket)
Bucket 是存儲(chǔ)Object的邏輯空間,每個(gè)Bucket之間的數(shù)據(jù)是相互隔離的,對(duì)用戶而言,相當(dāng)于存放文件的頂層文件夾,這篇文章主要介紹了SpringBoot整合Minio的過程(支持公有及私有bucket),需要的朋友可以參考下2025-03-03
如何在Java中使用org.json和JSON-B解析與編寫JSON
JSON-Java是用于在Java中讀取和寫入JSON文檔的參考應(yīng)用程序,這篇文章主要介紹了如何在Java中使用org.json和JSON-B解析與編寫JSON的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2025-06-06
springboot CompletableFuture并行計(jì)算及使用方法
CompletableFuture基于 Future 和 CompletionStage 接口,利用線程池、回調(diào)函數(shù)、異常處理、組合操作等機(jī)制,提供了強(qiáng)大而靈活的異步編程功能,這篇文章主要介紹了springboot CompletableFuture并行計(jì)算及使用方法,需要的朋友可以參考下2024-05-05

