Spring學(xué)習(xí)之動態(tài)代理

Spring學(xué)習(xí)之動態(tài)代理

前言

動態(tài)代理,是一種通過運行時操作字節(jié)碼,以達到增強類的功能的技術(shù),也是Spring AOP操作的基礎(chǔ),關(guān)于AOP的內(nèi)容,將在后面的筆記中詳細(xì)講解,本小節(jié)主要是理清楚動態(tài)代理,畢竟,Spring的AOP是基于動態(tài)代理技術(shù),對動態(tài)代理技術(shù)有所了解,對于學(xué)習(xí)Spring AOP也會有幫助

動態(tài)代理技術(shù)詳解

動態(tài)代理,現(xiàn)在主要是用于增強類的功能,同時由于是具有動態(tài)性,所以避免了需要頻繁創(chuàng)建類的操作,同時,也使得原有的代碼在不需要改變的情況下,對類的功能進行增強,主要的動態(tài)代理技術(shù)有:通過實現(xiàn)目標(biāo)接口,重寫其方法,以增強其能力,典型的以JDK動態(tài)代理為代表;或者,通過繼承類,重寫其方法以增強其能力,典型的以CGLib為代表,這兩種技術(shù)分別從不同的方向來對類的能力進行擴充,接下來來具體看下這兩種技術(shù)的特點以及差異。

基于JDK動態(tài)代理

基于JDK的動態(tài)代理技術(shù),其主要特點就是目標(biāo)類,也就是需要被代理的類,必須有接口,并且代理類必須實現(xiàn)跟它一樣的接口,從而來起到代理其事務(wù)的功能,具體使用如下代碼所示,假設(shè)有一個UserService類,主要用于負(fù)責(zé)用戶的登錄和退出,同時,有個日志類,負(fù)責(zé)記錄用戶的操作信息,直接將信息日志寫在對應(yīng)的UserService實現(xiàn)類中,可以達到目的,但顯然這種方式不是很合理,特別是在UserService有很多個方法需要做日志記錄的時候,就會使得日志記錄代碼遍布整個UserService,不僅使得代碼的冗余很大,而且當(dāng)需要進行修改的時候,也需要逐個修改,非常麻煩,這個時候,采用動態(tài)代理技術(shù)就是一種非常好的方法了。


/**
 * UserService接口
 */
interface UserService{

    void login();
    void logout();
}

/**
 * UseService實現(xiàn)類
 */
class UserServiceImpl implements UserService{

    @Override
    public void login() {
        System.out.println("someone login....");
    }

    @Override
    public void logout() {
        System.out.println("someone logout....");
    }
}


/**
 * 實現(xiàn)InvocationHandle接口,用于織入所要增強的代碼
 */
class UserServiceHandle implements InvocationHandler{

    private UserService userService;

    public UserServiceHandle(UserService userService) {
        this.userService = userService;
    }

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

        LogService.info();
        Object object = method.invoke(userService, args);
        LogService.info();
        return object;
    }
}

/**
 * 代理類工廠,用于產(chǎn)生UseService類的代理類
 */
class ProxyFactory{

    public static UserService getProxyObject(UserService userService){

        // 使用JDK動態(tài)代理技術(shù)來創(chuàng)建對應(yīng)的代理類
        return (UserService) Proxy.newProxyInstance(
                userService.getClass().getClassLoader(),
                userService.getClass().getInterfaces(),
                new UserServiceHandle(userService)
        );
    }
}

這樣,當(dāng)我們需要使用UseService類的時候,只需要從ProxyFactory中獲取即可,而且獲取的對象是UserService對象的代理類,也就是說,獲得的對象是UserService對象的增強版

基于CGLib的動態(tài)代理技術(shù)

從上面的ProxyFactory工廠中可以看到,在使用JDK進行創(chuàng)建動態(tài)代理對象的時候,需要為其提供接口,或者說,如果所要增強的目標(biāo)類沒有實現(xiàn)任何接口,則JDK動態(tài)代理技術(shù)是無法為其創(chuàng)建對應(yīng)的代理對象的,這是JDK動態(tài)代理技術(shù)的一種缺點,而CGLib動態(tài)代理技術(shù)則恰好彌補了這個缺點,CGLib動態(tài)代理技術(shù)使用的是繼承該類的方式,從而避免了需要接口的缺陷,具體使用如下所示,注意,需要導(dǎo)入對應(yīng)的依賴文件


/**
 * 基于CGLib的動態(tài)代理技術(shù)
 * 注意這里需要實現(xiàn)MethodInterceptor接口
 */
class ProxyFactory implements MethodInterceptor{

    // 提供對應(yīng)的增強操作類
    private  Enhancer enhancer = new Enhancer();

    public UserService getProxyObject(Class clazz){
        // 設(shè)置所要增強的類的父類
        enhancer.setSuperclass(clazz);
        // 設(shè)置回調(diào)對象
        enhancer.setCallback(this);
        // 創(chuàng)建對應(yīng)的對象
        return (UserService) enhancer.create();
    }

    // 實現(xiàn)攔截方法,用于攔截對應(yīng)的方法,并且對對應(yīng)的方法進行增強
    // 參數(shù)含義:傳入的對象, Method對象,方法的參數(shù),進行代理后的Method對象
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

        LogService.info();
        // 這里需要注意,由于methodProxy對象是增強后的Method對象,所以這里需要調(diào)用的
        // 是methodProxy父類的方法,也就是所以增強的類的方法,以實現(xiàn)原來的功能
        Object object = methodProxy.invokeSuper(o, objects);
        LogService.info();
        return object;
    }
}

可以看到,使用CGLib動態(tài)代理技術(shù)可以在不需要實現(xiàn)接口的情況下東塔為對象創(chuàng)建代理對象,在很大程度上彌補了JDK動態(tài)代理技術(shù)的缺點,不過由于CGLib動態(tài)代理技術(shù)是采用繼承目標(biāo)類的方式,所以也存在一些問題,比如說,對于final以及private修飾的方法是無法為其增強的,這里還需要注意一下。

總結(jié)

動態(tài)代理技術(shù)是實現(xiàn)AOP技術(shù)的基礎(chǔ),也是一種很方便地實現(xiàn)方式,常用的動態(tài)代理技術(shù)有基于JDK動態(tài)代理技術(shù)以及基于CGLib的動態(tài)代理技術(shù),兩種技術(shù)各有千秋,也都各有缺點基于JDK的動態(tài)代理技術(shù)需要為其提供接口,基于CGLib的動態(tài)代理技術(shù)不能為final以及private修飾的方法進行增強,在使用的時候需要根據(jù)具體進行進行合理選擇。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 今天內(nèi)容介紹 Spring框架的IOC基于注解的方式 注解類型 注解生命周期 Spring框架整合JUnit單元測...
  • 0.前言 本文主要想闡述的問題如下:什么動態(tài)代理(AOP)以及如何用JDK的Proxy和InvocationHan...
    SYFHEHE閱讀 2,401評論 1 7
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評論 19 139
  • 在理解Spring AOP以及理清它與Aspect和cglib之間關(guān)系之前,有很多基礎(chǔ)工作要做,比如,先對代理模式...
    maxwellyue閱讀 1,530評論 0 5
  • 從三月份找實習(xí)到現(xiàn)在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發(fā)崗...
    時芥藍(lán)閱讀 42,762評論 11 349

友情鏈接更多精彩內(nèi)容