天天看點

spring aop注解實作

aop一般是注解和xml兩種方式

注解用的更多些

aop是面向切面程式設計,有别于面向對象。

場景在于加密、日志記錄等都可以使用面向切面程式設計或者過濾器、攔截器。

spring aop實作是通過動态代理來實作的

概述

Spring的AOP實作是通過動态代理實作的。如果我們為Spring的一個bean配置了AOP切面,那麼Spring在建立這個bean的時候,實際上建立的是這個bean的一個代理對象,我們後續對bean中方法的調用,實際上調用的是代理類重寫的代理方法。

而Spring的AOP使用了兩種動态代理,分别是

JDK的動态代理;

CGLib的動态代理。

JDK動态代理

Spring預設使用JDK的動态代理實作AOP,類如果實作了接口,Spring就會使用這種方式實作動态代理。JDK實作動态代理需要兩個元件

首先第一個就是InvocationHandler接口。我們在使用JDK的動态代理時,需要編寫一個類,去實作這個接口,然後重寫invoke方法,這個方法其實就是我們提供的代理方法。

然後JDK動态代理需要使用的第二個元件就是Proxy這個類,我們可以通過這個類的newProxyInstance方法,傳回一個代理對象。生成的代理類實作了原來那個類的所有接口,并對接口的方法進行了代理,我們通過代理對象調用這些方法時,底層将通過反射,調用我們實作的invoke方法。

CGLib動态代理

JDK的動态代理存在限制,那就是被代理的類必須是一個實作了接口的類,代理類需要實作相同的接口,代理接口中聲明的方法。

若需要代理的類沒有實作接口,此時JDK的動态代理将沒有辦法使用,于是Spring會使用CGLib的動态代理來生成代理對象。

CGLib直接操作位元組碼,生成類的子類,重寫類的方法完成代理。

JDK的動态代理

原理

JDK的動态代理是基于反射實作。JDK通過反射,生成一個代理類,這個代理類實作了原來那個類的全部接口,并對接口中定義的所有方法進行了代理。當我們通過代理對象執行原來那個類的方法時,代理類底層會通過反射機制,回調我們實作的InvocationHandler接口的invoke方法。并且這個代理類是Proxy類的子類

優點

JDK動态代理是JDK原生的,不需要任何依賴即可使用;

通過反射機制生成代理類的速度要比CGLib操作位元組碼生成代理類的速度更快;

缺點

如果要使用JDK動态代理,被代理的類必須實作了接口,否則無法代理;

JDK動态代理無法為沒有在接口中定義的方法實作代理,假設我們有一個實作了接口的類,我們為它的一個不屬于接口中的方法配置了切面,Spring仍然會使用JDK的動态代理,但是由于配置了切面的方法不屬于接口,為這個方法配置的切面将不會被織入。

JDK動态代理執行代理方法時,需要通過反射機制進行回調,此時方法執行的效率比較低;

CGLib動态代理

原理

CGLib實作動态代理的原理是,底層采用了ASM位元組碼生成架構,直接對需要代理的類的位元組碼進行操作,生成這個類的一個子類,并重寫了類的所有可以重寫的方法,在重寫的過程中,将我們定義的額外的邏輯(切面)織入到方法中,對方法進行了增強。

通過位元組碼操作生成的代理類,和我們自己編寫并編譯後的類沒有什麼差別。

優點

使用CGLib代理的類,不需要實作接口,因為CGLib生成的代理類是直接繼承自需要被代理的類;

CGLib生成的代理類是原來那個類的子類,這就意味着這個代理類可以為原來那個類中,所有能夠被子類重寫的方法進行代理;

CGLib生成的代理類,和我們自己編寫并編譯的類沒有太大差別,對方法的調用和直接調用普通類的方式一緻,是以CGLib執行代理方法的效率要高于JDK的動态代理;

缺點

由于CGLib的代理類使用的是繼承,這也就意味着如果需要被代理的類是一個final類,則無法使用CGLib代理;

由于CGLib實作代理方法的方式是重寫父類的方法,是以無法對final方法,或者private方法進行代理,因為子類無法重寫這些方法;

CGLib生成代理類的方式是通過操作位元組碼,這種方式生成代理類的速度要比JDK通過反射生成代理類的速度更慢;

概述

Spring的AOP實作是通過動态代理實作的。如果我們為Spring的一個bean配置了AOP切面,那麼Spring在建立這個bean的時候,實際上建立的是這個bean的一個代理對象,我們後續對bean中方法的調用,實際上調用的是代理類重寫的代理方法。

而Spring的AOP使用了兩種動态代理,分别是

JDK的動态代理;

CGLib的動态代理。

JDK動态代理

Spring預設使用JDK的動态代理實作AOP,類如果實作了接口,Spring就會使用這種方式實作動态代理。JDK實作動态代理需要兩個元件

首先第一個就是InvocationHandler接口。我們在使用JDK的動态代理時,需要編寫一個類,去實作這個接口,然後重寫invoke方法,這個方法其實就是我們提供的代理方法。

然後JDK動态代理需要使用的第二個元件就是Proxy這個類,我們可以通過這個類的newProxyInstance方法,傳回一個代理對象。生成的代理類實作了原來那個類的所有接口,并對接口的方法進行了代理,我們通過代理對象調用這些方法時,底層将通過反射,調用我們實作的invoke方法。

CGLib動态代理

JDK的動态代理存在限制,那就是被代理的類必須是一個實作了接口的類,代理類需要實作相同的接口,代理接口中聲明的方法。

若需要代理的類沒有實作接口,此時JDK的動态代理将沒有辦法使用,于是Spring會使用CGLib的動态代理來生成代理對象。

CGLib直接操作位元組碼,生成類的子類,重寫類的方法完成代理。

JDK的動态代理

原理

JDK的動态代理是基于反射實作。JDK通過反射,生成一個代理類,這個代理類實作了原來那個類的全部接口,并對接口中定義的所有方法進行了代理。當我們通過代理對象執行原來那個類的方法時,代理類底層會通過反射機制,回調我們實作的InvocationHandler接口的invoke方法。并且這個代理類是Proxy類的子類

優點

JDK動态代理是JDK原生的,不需要任何依賴即可使用;

通過反射機制生成代理類的速度要比CGLib操作位元組碼生成代理類的速度更快;

缺點

如果要使用JDK動态代理,被代理的類必須實作了接口,否則無法代理;

JDK動态代理無法為沒有在接口中定義的方法實作代理,假設我們有一個實作了接口的類,我們為它的一個不屬于接口中的方法配置了切面,Spring仍然會使用JDK的動态代理,但是由于配置了切面的方法不屬于接口,為這個方法配置的切面将不會被織入。

JDK動态代理執行代理方法時,需要通過反射機制進行回調,此時方法執行的效率比較低;

CGLib動态代理

原理

CGLib實作動态代理的原理是,底層采用了ASM位元組碼生成架構,直接對需要代理的類的位元組碼進行操作,生成這個類的一個子類,并重寫了類的所有可以重寫的方法,在重寫的過程中,将我們定義的額外的邏輯(切面)織入到方法中,對方法進行了增強。

通過位元組碼操作生成的代理類,和我們自己編寫并編譯後的類沒有什麼差別。

優點

使用CGLib代理的類,不需要實作接口,因為CGLib生成的代理類是直接繼承自需要被代理的類;

CGLib生成的代理類是原來那個類的子類,這就意味着這個代理類可以為原來那個類中,所有能夠被子類重寫的方法進行代理;

CGLib生成的代理類,和我們自己編寫并編譯的類沒有太大差別,對方法的調用和直接調用普通類的方式一緻,是以CGLib執行代理方法的效率要高于JDK的動态代理;

缺點

由于CGLib的代理類使用的是繼承,這也就意味着如果需要被代理的類是一個final類,則無法使用CGLib代理;

由于CGLib實作代理方法的方式是重寫父類的方法,是以無法對final方法,或者private方法進行代理,因為子類無法重寫這些方法;

CGLib生成代理類的方式是通過操作位元組碼,這種方式生成代理類的速度要比JDK通過反射生成代理類的速度更慢;