動態代理有哪些神仙

程序員小迷 2024-04-25 15:42:57

動態代理是一種以動態方式創建代理對象的設計模式,它可以在運行時根據需要實現代理對象的創建、調用和銷毀,在使用時再創建代理類和實例。

動態代理分爲基于接口的動態代理(被代理的對象必須實現一個或多個接口)和基于類的動態代理(這種代理方式不依賴于接口,而是直接基于類來實現,基于的類不能爲final)。

根據實現方式的不同,動態代理主要可以分爲以下幾類:

1.JDK自帶的動態代理

這種代理方式主要依賴于Java的反射機制。在Java中,動態代理主要通過兩個核心類來實現:Proxy和InvocationHandler。Proxy類用于動態創建代理類,而InvocationHandler接口負責處理代理對象的方法調用。通過實現InvocationHandler接口,開發人員可以在代理對象的方法調用前後添加自定義的邏輯,實現對原始對象的控制和增強。這種代理方式要求被代理的對象必須實現一個或多個接口。這種代理是基于接口的動態代理。

1)優點

只需要1個動態代理類就可以解決創建多個靜態代理的問題,避免重複、多余代碼,更強的靈活性。

2)缺點

效率低,相比靜態代理中 直接調用目標對象方法,動態代理則需要先通過Java反射機制 從而 間接調用目標對象的方法,因爲 Java 的單繼承特性(每個代理類都繼承了 Proxy 類),即只能針對接口 創建 代理類,不能針對類創建代理類。

// 定義一個接口

interface MyInterface {

void execute();

}

// 實現InvocationHandler接口

class MyInvocationHandler implements InvocationHandler {

private Object target;

public MyInvocationHandler(Object target) {

this.target = target;

}

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

//調用目標對象的方法之前處理

System.out.println("Before method invocation");

// 調用目標對象的方法

Object result = method.invoke(target, args);

//調用目標對象的方法之後處理

System.out.println("After method invocation");

return result;

}

}

public JDKDynamicProxyTest {

public static void main(String[] args) {

// 創建目標對象

MyInterface myObject = new MyInterface() {

@Override

public void execute() {

System.out.println("do something...");

}

};

// 創建InvocationHandler實例

InvocationHandler handler = new MyInvocationHandler(myObject);

// 創建動態代理實例

MyInterface proxy = (MyInterface) Proxy.newProxyInstance(

MyInterface.class.getClassLoader(),

new Class<?>[]{MyInterface.class},

handler

);

// 通過代理實例調用方法,會觸發InvocationHandler的invoke方法

proxy.execute();

}

}

2.CGLib動態代理

CGLib(Code Generation Library)是一個強大的高性能的代碼生成庫,它可以在運行期擴展Java類與實現Java接口。它廣泛的被許多AOP的框架使用,例如Spring AOP和dynaop,爲它們提供方法的攔截。

CGLib 代理可以代理沒有接口的類,但無法代理 final 類和方法。

public CGLibProxyTest implements MethodInterceptor {

private Object target; // 代理對象

public Object getInstance(Object target) {

this.target = target;

Enhancer enhancer = new Enhancer();

// 設置父類

enhancer.setSuperclass(this.target.getClass());

// 回調方法

enhancer.setCallback(this);

// 創建代理對象

return enhancer.create();

}

@Override

public Object intercept(Object o, Method method,

Object[] objects, MethodProxy methodProxy) throws Throwable {

System.out.println("方法調用前處理");

Object result = methodProxy.invokeSuper(o, objects); // 執行方法調用

return result;

}

}

3.Javassist動態代理

Javassist(Java programming assistant)是一個開源的分析、編輯和創建Java字節碼的庫。它已經被許多其他開源項目所使用,例如CGLib。Javassist是jboss的一個子項目,其主要的優點,在于簡單快速。直接使用java編碼的形式,而不需要了解虛擬機指令,就能動態改變類的結構,或者動態生成類。

public JavassistTest {

public static void main(String[] args) throws Exception {

// 獲取Javassist的ClassPool

ClassPool pool = ClassPool.getDefault();

// 創建一個新的類

CtClass cc = new CtClass(pool, "com.xx.User");

// 爲新類添加一個新方法

CtMethod newMethod = new CtMethod(CtClass.voidType, "newMethod", new CtClass[]{}, cc);

newMethod.setModifier(Modifier.PUBLIC);

newMethod.setBody("{ System.out.println(\"Hello world!\"); }");

cc.addMethod(newMethod);

// 生成類的.class文件

cc.writeFile("/home/xx/directory");

}

}

4.AspectJ

AspectJ是一個強大的面向切面編程(AOP)框架,它可以在編譯時或運行時修改Java字節碼,從而實現更爲複雜的代理功能。

@Aspect

public UserAspect {

// 定義切點,匹配 TestService 類中的所有方法

@Pointcut("execution(* com.xx.TestService.*(..))")

public void aMethod() {}

// 在方法執行前執行

@Before("aMethod()")

public void beforeMethod(JoinPoint joinPoint) {

System.out.println("Before method: " + joinPoint.getSignature().getName());

}

// 在方法執行後執行

@After("aMethod()")

public void afterMethod(JoinPoint joinPoint) {

System.out.println("After method: " + joinPoint.getSignature().getName());

}

}

5.Spring AOP

Spring框架也提供了對AOP的支持,它基于AspectJ的切點表達式語言,但不需要使用AspectJ的編譯器或織入器。Spring AOP通過代理模式實現,可以在運行時爲指定的bean創建代理。

Spring 框架中同時使用了兩種動態代理 JDK Proxy 和 CGLib,當 Bean 實現了接口時,Spring 就會使用 JDK Proxy,在沒有實現接口時就會使用 CGLib,我們也可以在配置中指定強制使用 CGLib,只需要在 Spring 配置中添加 <aop:aspectj-autoproxy proxy-target-class="true"/> 即可。

@Aspect

public AroundTest {

@Pointcut("execution(* com.xx.ClassName.perform(..))")

public void perform() {

}

@Around("perform()")

public void executeMethod(ProceedingJoinPoint joinPoint) {

try {

System.out.println("abc");

joinPoint.proceed();

System.out.println("over");

} catch (Throwable throwable) {

System.out.println("exception occurred");

} finally {

System.out.println("finish");

}

}

}

6.ASM 字節碼操作庫

ASM 是一個低級的字節碼操作和分析框架,可以直接生成或修改字節碼文件。雖然不是專門用于動態代理的庫,但可以通過它來手動編寫字節碼生成器,實現高度定制化的代理功能。

7.ByteBuddy

ByteBuddy 是一個更高層次的代碼生成和操作庫,它提供了簡潔的 API 來創建和修改 Java 類和方法。它提供了更強大的代理功能,包括基于接口和基于類的代理,以及對方法攔截、字段操作等的全面支持。ByteBuddy 還支持與 Java 動態代理、CGLib等現有代理機制的無縫集成。

@Test

public void testByteBuddy() throws IllegalAccessException, InstantiationException {

String s = new ByteBuddy()

// 指定父類

.subclass(Object.class)

// 指定生成類的名稱

.name("Plant")

// 按名稱 攔截該類的 toString()

.method(ElementMatchers.named("toString"))

// 攔截方法調用 返回固定值

.intercept(FixedValue.value("Hello plant!"))

// 産生字節碼

.make()

//Loader 加載字節碼到內存

.load(ByteBuddy.class.getClassLoader())

// 獲得class對象

.getLoaded()

.newInstance()

.toString();

System.out.println(s);

}

總之,動態代理的主要目的是在不修改原始類和接口的情況下,對原始對象進行額外的操作或增強。

微風不燥,陽光正好,你就像風一樣經過這裏,願你停留的片刻溫暖舒心。

我是程序員小迷(致力于C、C++、Java、Kotlin、Android、Shell、JavaScript、TypeScript、Python等編程技術的技巧經驗分享),若作品對您有幫助,請關注、分享、點贊、收藏、在看、喜歡,您的支持是我們爲您提供幫助的最大動力。

歡迎關注。助您在編程路上越走越好!

0 阅读:63

程序員小迷

簡介:致力于Android、C等編程技術的技巧經驗分享