自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

深入探究動態(tài)代理:JDK 與 CGLIB 的實(shí)現(xiàn)奧秘與差異剖析

開發(fā) 前端
當(dāng)目標(biāo)對象有接口且注重開發(fā)便捷性、遵循接口編程規(guī)范時(shí),JDK 動態(tài)代理是首選。例如在標(biāo)準(zhǔn)的企業(yè)級 Java 應(yīng)用開發(fā)中,服務(wù)層接口常使用 JDK 代理實(shí)現(xiàn)日志記錄、權(quán)限驗(yàn)證等 AOP 功能,與 Spring 等框架無縫集成。

在 Java 編程領(lǐng)域,動態(tài)代理是一種強(qiáng)大的設(shè)計(jì)模式,它允許開發(fā)者在運(yùn)行時(shí)創(chuàng)建代理對象,對目標(biāo)對象的行為進(jìn)行增強(qiáng)、監(jiān)控或修改,而無需在編譯期就確定代理的具體邏輯。這一特性在諸多框架中廣泛應(yīng)用,如 Spring AOP(面向切面編程),為實(shí)現(xiàn)橫切面關(guān)注點(diǎn)的模塊化提供了關(guān)鍵支撐。

一、JDK 動態(tài)代理實(shí)現(xiàn)原理

JDK 動態(tài)代理依托于 Java 反射機(jī)制構(gòu)建,核心涉及  java.lang.reflect  包下的幾個(gè)重要類。

首先,需要定義一個(gè)接口,該接口代表目標(biāo)對象與代理對象共同遵循的行為規(guī)范。假設(shè)我們有一個(gè)簡單的圖形繪制接口  Shape :

public interface Shape {
    void draw();
}
接著是目標(biāo)類實(shí)現(xiàn)此接口,例如  Circle  類:
public class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a circle");
    }
}

創(chuàng)建代理對象的關(guān)鍵在于實(shí)現(xiàn)  java.lang.reflect.InvocationHandler  接口,它定義了一個(gè)  invoke  方法,用于處理代理對象上所有方法的調(diào)用邏輯:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class ShapeInvocationHandler implements InvocationHandler {
    private final Object target;
    public ShapeInvocationHandler(Object target) {
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 在目標(biāo)方法調(diào)用前添加額外邏輯,如日志記錄
        System.out.println("Before method invocation");
        Object result = method.invoke(target, args);
        // 在目標(biāo)方法調(diào)用后添加額外邏輯,如性能統(tǒng)計(jì)
        System.out.println("After method invocation");
        return result;
    }
}

最后,通過  java.lang.reflect.Proxy  類生成代理對象:

import java.lang.reflect.Proxy;
public class Main {
    public static void main(String[] args) {
        Shape circle = new Circle();
        ShapeInvocationHandler handler = new ShapeInvocationHandler(circle);
        Shape proxyShape = (Shape) Proxy.newProxyInstance(
                Shape.class.getClassLoader(),
                new Class[]{Shape.class},
                handler);
        proxyShape.draw();
    }
}

在上述代碼中,當(dāng)調(diào)用代理對象  proxyShape  的  draw  方法時(shí),實(shí)際上控制權(quán)轉(zhuǎn)移到  ShapeInvocationHandler  的  invoke  方法,在此可以靈活插入前置、后置邏輯,實(shí)現(xiàn)對目標(biāo)對象方法的動態(tài)增強(qiáng)。

二、CGLIB 動態(tài)代理實(shí)現(xiàn)原理

CGLIB(Code Generation Library)動態(tài)代理采取了截然不同的字節(jié)碼生成策略。它不依賴接口,而是直接對目標(biāo)類進(jìn)行字節(jié)碼擴(kuò)展。

引入 CGLIB 相關(guān)依賴后,以同樣的  Circle  類為例(此時(shí)無需實(shí)現(xiàn)接口):

public class Circle {
    public void draw() {
        System.out.println("Drawing a circle");
    }
}

創(chuàng)建一個(gè)實(shí)現(xiàn)  net.sf.cglib.proxy.MethodInterceptor  接口的攔截器類,它類似于 JDK 代理中的  InvocationHandler :

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibShapeInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("CGLIB: Before method call");
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("CGLIB: After method call");
        return result;
    }
}

利用  net.sf.cglib.proxy.Enhancer  類生成代理對象:

import net.sf.cglib.proxy.Enhancer;
public class Main {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Circle.class);
        enhancer.setCallback(new CglibShapeInterceptor());
        Circle proxyCircle = (Circle) enhancer.create();
        proxyCircle.draw();
    }
}

這里, Enhancer  通過修改目標(biāo)類的字節(jié)碼,在原始方法執(zhí)行前后織入自定義邏輯,生成的代理對象繼承自目標(biāo)類,能直接調(diào)用目標(biāo)類非  private  的方法,無需像 JDK 代理那樣依賴接口。

三、JDK 動態(tài)代理與 CGLIB 動態(tài)代理的區(qū)別

(一)實(shí)現(xiàn)基礎(chǔ)

- JDK 動態(tài)代理:基于接口實(shí)現(xiàn),要求目標(biāo)對象必須實(shí)現(xiàn)至少一個(gè)接口。它利用 Java 反射在運(yùn)行時(shí)動態(tài)生成代理類,該代理類同樣實(shí)現(xiàn)目標(biāo)對象的接口,通過接口回調(diào)機(jī)制將方法調(diào)用轉(zhuǎn)發(fā)至  InvocationHandler  處理。

- CGLIB 動態(tài)代理:基于繼承機(jī)制,直接操作目標(biāo)類的字節(jié)碼,生成目標(biāo)類的子類作為代理對象。它通過重寫子類方法,在其中插入攔截邏輯,調(diào)用父類(即目標(biāo)類)原始方法實(shí)現(xiàn)功能,無需目標(biāo)對象有接口實(shí)現(xiàn)。

(二)性能表現(xiàn)

- 在創(chuàng)建代理對象階段,JDK 動態(tài)代理相對較快,因?yàn)樗恍杌诮涌谛畔⒗梅瓷渖珊唵未眍惤Y(jié)構(gòu);而 CGLIB 需要通過復(fù)雜的字節(jié)碼生成技術(shù)創(chuàng)建子類,耗時(shí)較長。

- 但在方法調(diào)用時(shí),JDK 動態(tài)代理由于每次都要經(jīng)過反射查找方法等操作,性能開銷較大;CGLIB 代理對象調(diào)用方法類似普通對象調(diào)用,因?yàn)榉椒ㄒ言谧止?jié)碼層面重寫優(yōu)化,若頻繁調(diào)用代理方法,CGLIB 在性能上更具優(yōu)勢。

(三)適用場景

- 當(dāng)目標(biāo)對象有接口且注重開發(fā)便捷性、遵循接口編程規(guī)范時(shí),JDK 動態(tài)代理是首選。例如在標(biāo)準(zhǔn)的企業(yè)級 Java 應(yīng)用開發(fā)中,服務(wù)層接口常使用 JDK 代理實(shí)現(xiàn)日志記錄、權(quán)限驗(yàn)證等 AOP 功能,與 Spring 等框架無縫集成。

- 若目標(biāo)對象沒有接口,或者需要對 final 修飾的類進(jìn)行代理(JDK 代理無法做到,因 final 類不可繼承),CGLIB 動態(tài)代理便能大顯身手。像一些遺留代碼改造、底層工具類增強(qiáng)場景,CGLIB 可突破接口限制,靈活實(shí)現(xiàn)代理需求。

JDK 動態(tài)代理與 CGLIB 動態(tài)代理各具特色,它們從不同角度為 Java 開發(fā)者提供了動態(tài)代理的實(shí)現(xiàn)路徑,深入理解二者原理與區(qū)別,有助于在面對復(fù)雜多變的編程需求時(shí),精準(zhǔn)選擇合適的代理方式,打造高效、健壯的 Java 應(yīng)用。

責(zé)任編輯:武曉燕 來源: 程序員conan
相關(guān)推薦

2024-01-04 07:42:44

JavaCGLIBJDK

2023-12-06 08:23:44

代理模式設(shè)計(jì)模式

2021-04-22 09:58:15

JDK代理動態(tài)

2022-09-01 10:40:29

SpringAOPJDK

2021-07-14 11:07:56

AOPJDKCglib

2024-03-07 08:22:51

Java機(jī)制元數(shù)據(jù)

2023-09-28 09:03:56

開源搜索分析引擎

2024-09-05 09:35:58

CGLIBSpring動態(tài)代理

2024-01-26 08:33:14

JDK17JDK11版本

2012-09-28 10:20:14

IBMdw

2010-08-16 10:25:23

DIVSPAN

2012-09-27 09:47:43

SpringJava面向?qū)ο?/a>

2010-09-01 09:29:51

CSS層疊CSS繼承

2020-10-25 17:11:29

JDK代理監(jiān)控

2023-06-07 07:43:57

數(shù)據(jù)庫JOIN類型

2024-01-31 23:47:17

i++++i編碼

2022-11-15 09:57:51

Java接口

2013-05-07 11:43:47

2010-09-30 09:16:04

cookieJ2ME

2011-11-24 21:03:10

ibmdw
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號