欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot中5種動態(tài)代理的實現(xiàn)方案

 更新時間:2025年04月22日 08:10:16   作者:風(fēng)象南  
在SpringBoot應(yīng)用中,動態(tài)代理被廣泛用于實現(xiàn)事務(wù)管理、緩存、安全控制、日志記錄等橫切關(guān)注點,下面小編為大家介紹一下SpringBoot中5種動態(tài)代理的實現(xiàn)方案吧

動態(tài)代理允許我們在不修改源代碼的情況下,為對象增加額外的行為。在SpringBoot應(yīng)用中,動態(tài)代理被廣泛用于實現(xiàn)事務(wù)管理、緩存、安全控制、日志記錄等橫切關(guān)注點。

1. JDK動態(tài)代理:Java原生的代理方案

實現(xiàn)原理

JDK動態(tài)代理是Java標(biāo)準(zhǔn)庫提供的代理機(jī)制,基于java.lang.reflect.Proxy類和InvocationHandler接口實現(xiàn)。它通過反射在運(yùn)行時動態(tài)創(chuàng)建接口的代理實例。

核心代碼示例

public class JdkDynamicProxyDemo {
    interface UserService {
        void save(User user);
        User find(Long id);
    }
    
    // 實現(xiàn)類
    static class UserServiceImpl implements UserService {
        @Override
        public void save(User user) {
            System.out.println("保存用戶: " + user.getName());
        }
        
        @Override
        public User find(Long id) {
            System.out.println("查找用戶ID: " + id);
            return new User(id, "用戶" + id);
        }
    }
    
    // 調(diào)用處理器
    static class LoggingInvocationHandler implements InvocationHandler {
        private final Object target;
        
        public LoggingInvocationHandler(Object target) {
            this.target = target;
        }
        
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("Before: " + method.getName());
            long startTime = System.currentTimeMillis();
            
            Object result = method.invoke(target, args);
            
            long endTime = System.currentTimeMillis();
            System.out.println("After: " + method.getName() + ", 耗時: " + (endTime - startTime) + "ms");
            
            return result;
        }
    }
    
    public static void main(String[] args) {
        // 創(chuàng)建目標(biāo)對象
        UserService userService = new UserServiceImpl();
        
        // 創(chuàng)建代理對象
        UserService proxy = (UserService) Proxy.newProxyInstance(
            userService.getClass().getClassLoader(),
            userService.getClass().getInterfaces(),
            new LoggingInvocationHandler(userService)
        );
        
        // 調(diào)用代理方法
        proxy.save(new User(1L, "張三"));
        User user = proxy.find(2L);
    }
}

優(yōu)點

  • JDK標(biāo)準(zhǔn)庫自帶:無需引入額外依賴,減少了項目體積
  • 生成代碼簡單:代理邏輯集中在InvocationHandler中,易于理解和維護(hù)
  • 性能相對穩(wěn)定:在JDK 8后的版本中,性能有明顯提升

局限性

  • 只能代理接口:被代理類必須實現(xiàn)接口,無法代理類
  • 反射調(diào)用開銷:每次方法調(diào)用都需通過反射機(jī)制,有一定性能損耗
  • 無法攔截final方法:無法代理被final修飾的方法

Spring中的應(yīng)用

在Spring中,當(dāng)Bean實現(xiàn)了接口時,默認(rèn)使用JDK動態(tài)代理。這可以通過配置修改:

@EnableAspectJAutoProxy(proxyTargetClass = false) // 默認(rèn)值為false,表示優(yōu)先使用JDK動態(tài)代理

2. CGLIB代理:基于字節(jié)碼的強(qiáng)大代理

實現(xiàn)原理

CGLIB(Code Generation Library)是一個強(qiáng)大的高性能字節(jié)碼生成庫,它通過繼承被代理類生成子類的方式實現(xiàn)代理。Spring從3.2版本開始將CGLIB直接集成到框架中。

核心代碼示例

public class CglibProxyDemo {
    // 不需要實現(xiàn)接口的類
    static class UserService {
        public void save(User user) {
            System.out.println("保存用戶: " + user.getName());
        }
        
        public User find(Long id) {
            System.out.println("查找用戶ID: " + id);
            return new User(id, "用戶" + id);
        }
    }
    
    // CGLIB方法攔截器
    static class LoggingMethodInterceptor implements MethodInterceptor {
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("Before: " + method.getName());
            long startTime = System.currentTimeMillis();
            
            // 調(diào)用原始方法
            Object result = proxy.invokeSuper(obj, args);
            
            long endTime = System.currentTimeMillis();
            System.out.println("After: " + method.getName() + ", 耗時: " + (endTime - startTime) + "ms");
            
            return result;
        }
    }
    
    public static void main(String[] args) {
        // 創(chuàng)建CGLIB代理
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserService.class);
        enhancer.setCallback(new LoggingMethodInterceptor());
        
        // 創(chuàng)建代理對象
        UserService proxy = (UserService) enhancer.create();
        
        // 調(diào)用代理方法
        proxy.save(new User(1L, "張三"));
        User user = proxy.find(2L);
    }
}

優(yōu)點

  • 可以代理類:不要求目標(biāo)類實現(xiàn)接口,應(yīng)用場景更廣泛
  • 性能較高:通過生成字節(jié)碼而非反射調(diào)用,方法調(diào)用性能優(yōu)于JDK代理
  • 功能豐富:支持多種回調(diào)類型,如LazyLoader、Dispatcher等
  • 集成到Spring:Spring框架已內(nèi)置CGLIB,無需額外依賴

局限性

  • 無法代理final類和方法:由于使用繼承機(jī)制,無法代理final修飾的類或方法
  • 構(gòu)造函數(shù)調(diào)用:在生成代理對象時會調(diào)用目標(biāo)類的構(gòu)造函數(shù),可能導(dǎo)致意外行為
  • 復(fù)雜性增加:生成的字節(jié)碼復(fù)雜度高,調(diào)試?yán)щy
  • 對Java版本敏感:在不同Java版本間可能存在兼容性問題

Spring中的應(yīng)用

在Spring中,當(dāng)Bean沒有實現(xiàn)接口或配置了proxyTargetClass=true時,使用CGLIB代理:

@EnableAspectJAutoProxy(proxyTargetClass = true) // 強(qiáng)制使用CGLIB代理

3. ByteBuddy:現(xiàn)代化的字節(jié)碼操作庫

實現(xiàn)原理

ByteBuddy是一個相對較新的字節(jié)碼生成和操作庫,設(shè)計更加現(xiàn)代化,API更加友好。它可以創(chuàng)建和修改Java類,而無需理解底層的JVM指令集。

核心代碼示例

public class ByteBuddyProxyDemo {
    interface UserService {
        void save(User user);
        User find(Long id);
    }
    
    static class UserServiceImpl implements UserService {
        @Override
        public void save(User user) {
            System.out.println("保存用戶: " + user.getName());
        }
        
        @Override
        public User find(Long id) {
            System.out.println("查找用戶ID: " + id);
            return new User(id, "用戶" + id);
        }
    }
    
    static class LoggingInterceptor {
        @RuntimeType
        public static Object intercept(@Origin Method method, @SuperCall Callable<?> callable,
                                       @AllArguments Object[] args) throws Exception {
            System.out.println("Before: " + method.getName());
            long startTime = System.currentTimeMillis();
            
            Object result = callable.call();
            
            long endTime = System.currentTimeMillis();
            System.out.println("After: " + method.getName() + ", 耗時: " + (endTime - startTime) + "ms");
            
            return result;
        }
    }
    
    public static void main(String[] args) throws Exception {
        UserService userService = new UserServiceImpl();
        
        // 創(chuàng)建ByteBuddy代理
        UserService proxy = new ByteBuddy()
            .subclass(UserServiceImpl.class)
            .method(ElementMatchers.any())
            .intercept(MethodDelegation.to(new LoggingInterceptor()))
            .make()
            .load(UserServiceImpl.class.getClassLoader())
            .getLoaded()
            .getDeclaredConstructor()
            .newInstance();
        
        // 調(diào)用代理方法
        proxy.save(new User(1L, "張三"));
        User user = proxy.find(2L);
    }
}

優(yōu)點

  • 流暢的API:提供鏈?zhǔn)骄幊田L(fēng)格,代碼更加可讀
  • 性能卓越:在多項基準(zhǔn)測試中,性能優(yōu)于CGLIB和JDK代理
  • 類型安全:API設(shè)計更注重類型安全,減少運(yùn)行時錯誤
  • 支持Java新特性:對Java 9+模塊系統(tǒng)等新特性有更好的支持
  • 功能豐富:支持方法重定向、字段訪問、構(gòu)造函數(shù)攔截等多種場景

局限性

  • 額外依賴:需要引入額外的依賴庫
  • 學(xué)習(xí)曲線:API雖然流暢,但概念較多,有一定學(xué)習(xí)成本
  • 在Spring中集成度不高:需要自定義配置才能在Spring中替代默認(rèn)代理機(jī)制

Spring中的應(yīng)用

ByteBuddy雖然不是Spring默認(rèn)的代理實現(xiàn),但可以通過自定義ProxyFactory來集成:

@Configuration
public class ByteBuddyProxyConfig {
    @Bean
    public AopConfigurer byteBuddyAopConfigurer() {
        return new AopConfigurer() {
            @Override
            public void configureProxyCreator(Object bean, String beanName) {
                // 配置ByteBuddy作為代理創(chuàng)建器
                // 實現(xiàn)細(xì)節(jié)略
            }
        };
    }
}

4. Javassist:更易用的字節(jié)碼編輯庫

實現(xiàn)原理

Javassist是一個開源的Java字節(jié)碼操作庫,提供了兩個層次的API:源代碼級別和字節(jié)碼級別。它允許開發(fā)者用簡單的Java語法直接編輯字節(jié)碼,無需深入了解JVM規(guī)范。

核心代碼示例

public class JavassistProxyDemo {
    interface UserService {
        void save(User user);
        User find(Long id);
    }
    
    static class UserServiceImpl implements UserService {
        @Override
        public void save(User user) {
            System.out.println("保存用戶: " + user.getName());
        }
        
        @Override
        public User find(Long id) {
            System.out.println("查找用戶ID: " + id);
            return new User(id, "用戶" + id);
        }
    }
    
    public static void main(String[] args) throws Exception {
        ProxyFactory factory = new ProxyFactory();
        
        // 設(shè)置代理接口
        factory.setInterfaces(new Class[] { UserService.class });
        
        // 創(chuàng)建方法過濾器
        MethodHandler handler = new MethodHandler() {
            private UserService target = new UserServiceImpl();
            
            @Override
            public Object invoke(Object self, Method method, Method proceed, Object[] args) throws Throwable {
                System.out.println("Before: " + method.getName());
                long startTime = System.currentTimeMillis();
                
                Object result = method.invoke(target, args);
                
                long endTime = System.currentTimeMillis();
                System.out.println("After: " + method.getName() + ", 耗時: " + (endTime - startTime) + "ms");
                
                return result;
            }
        };
        
        // 創(chuàng)建代理對象
        UserService proxy = (UserService) factory.create(new Class<?>[0], new Object[0], handler);
        
        // 調(diào)用代理方法
        proxy.save(new User(1L, "張三"));
        User user = proxy.find(2L);
    }
}

優(yōu)點

  • 源代碼級API:可以不懂字節(jié)碼指令集,以Java代碼形式操作字節(jié)碼
  • 輕量級庫:相比其他字節(jié)碼庫體積較小
  • 功能全面:支持創(chuàng)建類、修改類、動態(tài)編譯Java源代碼等
  • 性能良好:生成的代理代碼性能接近CGLIB
  • 長期維護(hù):庫歷史悠久,穩(wěn)定可靠

局限性

  • API不夠直觀:部分API設(shè)計較為古老,使用不如ByteBuddy流暢
  • 文檔不足:相比其他庫,文檔和示例較少
  • 內(nèi)存消耗:在操作大量類時可能消耗較多內(nèi)存

Spring中的應(yīng)用

Javassist不是Spring默認(rèn)的代理實現(xiàn),但一些基于Spring的框架使用它實現(xiàn)動態(tài)代理,如Hibernate:

// 自定義Javassist代理工廠示例
public class JavassistProxyFactory implements ProxyFactory {
    @Override
    public Object createProxy(Object target, Interceptor interceptor) {
        // Javassist代理實現(xiàn)
        // ...
    }
}

5. AspectJ:完整的AOP解決方案

實現(xiàn)原理

AspectJ是一個完整的AOP框架,與前面的動態(tài)代理方案不同,它提供兩種方式:

  • 編譯時織入:在編譯源代碼時直接修改字節(jié)碼
  • 加載時織入:在類加載到JVM時修改字節(jié)碼

AspectJ擁有專門的切面語言,功能比Spring AOP更強(qiáng)大。

核心代碼示例

AspectJ切面定義:

@Aspect
public class LoggingAspect {
    @Before("execution(* com.example.service.UserService.*(..))")
    public void logBefore(JoinPoint joinPoint) {
        System.out.println("Before: " + joinPoint.getSignature().getName());
    }
    
    @Around("execution(* com.example.service.UserService.*(..))")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("Before: " + joinPoint.getSignature().getName());
        long startTime = System.currentTimeMillis();
        
        Object result = joinPoint.proceed();
        
        long endTime = System.currentTimeMillis();
        System.out.println("After: " + joinPoint.getSignature().getName() + 
                          ", 耗時: " + (endTime - startTime) + "ms");
        
        return result;
    }
}

Spring配置AspectJ:

@Configuration
@EnableAspectJAutoProxy
public class AspectJConfig {
    @Bean
    public LoggingAspect loggingAspect() {
        return new LoggingAspect();
    }
}

編譯時織入配置(maven):

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>aspectj-maven-plugin</artifactId>
    <version>1.14.0</version>
    <configuration>
        <complianceLevel>11</complianceLevel>
        <source>11</source>
        <target>11</target>
        <aspectLibraries>
            <aspectLibrary>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aspects</artifactId>
            </aspectLibrary>
        </aspectLibraries>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>compile</goal>
            </goals>
        </execution>
    </executions>
</plugin>

優(yōu)點

  • 功能最全面:支持幾乎所有AOP場景,包括構(gòu)造函數(shù)、字段訪問、異常等
  • 性能最優(yōu):編譯時織入無運(yùn)行時開銷,性能接近原生代碼
  • 完整語言支持:有專門的切面語言和語法,表達(dá)能力強(qiáng)
  • 更靈活的切入點:可以對接口、實現(xiàn)類、構(gòu)造函數(shù)、字段等定義切面

局限性

  • 復(fù)雜度高:學(xué)習(xí)曲線陡峭,需要掌握AspectJ語法
  • 構(gòu)建過程改變:需要特殊的編譯器或類加載器
  • 調(diào)試難度增加:修改后的字節(jié)碼可能難以調(diào)試

Spring中的應(yīng)用

Spring可以通過以下方式使用AspectJ:

proxy-target-class模式:使用Spring AOP但CGLIB生成的代理

@EnableAspectJAutoProxy(proxyTargetClass = true)

LTW(Load-Time Weaving)模式:在類加載時織入

@EnableLoadTimeWeaving

編譯時織入:需要配置AspectJ編譯器

使用場景對比與選擇建議

代理實現(xiàn)最適用場景不適用場景
JDK動態(tài)代理基于接口的簡單代理,輕量級應(yīng)用沒有實現(xiàn)接口的類,性能敏感場景
CGLIB沒有實現(xiàn)接口的類,需要兼顧性能和便捷性final類/方法,高安全性環(huán)境
ByteBuddy現(xiàn)代化項目,關(guān)注性能優(yōu)化,復(fù)雜代理邏輯追求最小依賴的簡單項目
Javassist需要動態(tài)生成/修改類的復(fù)雜場景API設(shè)計敏感項目,初學(xué)者
AspectJ企業(yè)級應(yīng)用,性能關(guān)鍵型場景,復(fù)雜AOP需求簡單項目,快速原型,學(xué)習(xí)成本敏感

到此這篇關(guān)于SpringBoot中5種動態(tài)代理的實現(xiàn)方案的文章就介紹到這了,更多相關(guān)SpringBoot動態(tài)代理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 基于Mybatis實現(xiàn)CRUD操作過程解析(xml方式)

    基于Mybatis實現(xiàn)CRUD操作過程解析(xml方式)

    這篇文章主要介紹了基于Mybatis實現(xiàn)CRUD操作過程解析(xml方式),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-11-11
  • Spring?Security過濾器鏈體系的實例詳解

    Spring?Security過濾器鏈體系的實例詳解

    這篇文章主要介紹了Spring?Security過濾器鏈體系,通過思維導(dǎo)圖可以很好的幫助大家理解配置類的相關(guān)知識,結(jié)合實例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-02-02
  • 一篇文章了解Jackson注解@JsonFormat及失效解決辦法

    一篇文章了解Jackson注解@JsonFormat及失效解決辦法

    這篇文章主要給大家介紹了關(guān)于如何通過一篇文章了解Jackson注解@JsonFormat及失效解決辦法的相關(guān)資料,@JsonFormat注解是一個時間格式化注解,用于格式化時間,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-11-11
  • Java如何將大文件切割成小文件

    Java如何將大文件切割成小文件

    這篇文章主要為大家詳細(xì)介紹了Java如何將大文件切割成小文件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-05-05
  • Springboot actuator應(yīng)用后臺監(jiān)控實現(xiàn)

    Springboot actuator應(yīng)用后臺監(jiān)控實現(xiàn)

    這篇文章主要介紹了Springboot actuator應(yīng)用后臺監(jiān)控實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-04-04
  • Java中finalize()詳解及用法

    Java中finalize()詳解及用法

    這篇文章主要介紹了Java中finalize()詳解及用法的相關(guān)資料,final是Java的關(guān)鍵字,它所表示的是“這部分是無法修改的”,需要的朋友可以參考下
    2017-03-03
  • Mybatis中@Param注解的作用說明

    Mybatis中@Param注解的作用說明

    這篇文章主要介紹了Mybatis中@Param注解的作用說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • idea代碼模板設(shè)置方式

    idea代碼模板設(shè)置方式

    這篇文章主要介紹了idea代碼模板設(shè)置方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • Java實現(xiàn)將列表數(shù)據(jù)導(dǎo)出為PDF文件并添加水印

    Java實現(xiàn)將列表數(shù)據(jù)導(dǎo)出為PDF文件并添加水印

    這篇文章主要為大家詳細(xì)介紹了如何使用Java實現(xiàn)把列表數(shù)據(jù)導(dǎo)出為PDF文件,同時加上PDF水印,文中的示例代碼講解詳細(xì),需要的可以參考下
    2024-02-02
  • springSecurity實現(xiàn)簡單的登錄功能

    springSecurity實現(xiàn)簡單的登錄功能

    這篇文章主要為大家詳細(xì)介紹了springSecurity實現(xiàn)簡單的登錄功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-09-09

最新評論