Spring AOP的實(shí)現(xiàn)方式

一、AOP是什么穗酥?AOP與攔截器的區(qū)別护赊?

太抽象的不說(shuō),如果你知道Struts2的攔截器迷扇,攔截器就是應(yīng)用的AOP的思想百揭,它用于攔截Action以進(jìn)行一些預(yù)處理或結(jié)果處理爽哎。而Spring的AOP是一種更通用的模式蜓席,可以攔截Spring管理的Bean,功能更強(qiáng)大课锌,適用范圍也更廣厨内,它是通過(guò)動(dòng)態(tài)代理與反射機(jī)制實(shí)現(xiàn)的。

二渺贤、AOP一些概念

1.通知(Advice)

通知定義了在切入點(diǎn)代碼執(zhí)行時(shí)間點(diǎn)附近需要做的工作雏胃。

Spring支持五種類型的通知:

Before(前) ?org.apringframework.aop.MethodBeforeAdvice

After-returning(返回后) org.springframework.aop.AfterReturningAdvice

After-throwing(拋出后) org.springframework.aop.ThrowsAdvice

Arround(周圍) org.aopaliance.intercept.MethodInterceptor

Introduction(引入) org.springframework.aop.IntroductionInterceptor

2.連接點(diǎn)(Joinpoint)

程序能夠應(yīng)用通知的一個(gè)“時(shí)機(jī)”,這些“時(shí)機(jī)”就是連接點(diǎn)志鞍,例如方法調(diào)用時(shí)瞭亮、異常拋出時(shí)、方法返回后等等固棚。

3.切入點(diǎn)(Pointcut)

通知定義了切面要發(fā)生的“故事”统翩,連接點(diǎn)定義了“故事”發(fā)生的時(shí)機(jī)仙蚜,那么切入點(diǎn)就定義了“故事”發(fā)生的地點(diǎn),例如某個(gè)類或方法的名稱厂汗,Spring中允許我們方便的用正則表達(dá)式來(lái)指定。

4.切面(Aspect)

通知、連接點(diǎn)蝶押、切入點(diǎn)共同組成了切面:時(shí)間狞悲、地點(diǎn)和要發(fā)生的“故事”。

5.引入(Introduction)

引入允許我們向現(xiàn)有的類添加新的方法和屬性(Spring提供了一個(gè)方法注入的功能)衷畦。

6.目標(biāo)(Target)

即被通知的對(duì)象栗涂,如果沒(méi)有AOP,那么通知的邏輯就要寫在目標(biāo)對(duì)象中霎匈,有了AOP之后它可以只關(guān)注自己要做的事戴差,解耦合!

7.代理(proxy)

應(yīng)用通知的對(duì)象铛嘱,詳細(xì)內(nèi)容參見(jiàn)設(shè)計(jì)模式里面的動(dòng)態(tài)代理模式暖释。

8.織入(Weaving)

把切面應(yīng)用到目標(biāo)對(duì)象來(lái)創(chuàng)建新的代理對(duì)象的過(guò)程,織入一般發(fā)生在如下幾個(gè)時(shí)機(jī):

(1)編譯時(shí):當(dāng)一個(gè)類文件被編譯時(shí)進(jìn)行織入墨吓,這需要特殊的編譯器才可以做的到球匕,例如AspectJ的織入編譯器;

(2)類加載時(shí):使用特殊的ClassLoader在目標(biāo)類被加載到程序之前增強(qiáng)類的字節(jié)代碼帖烘;

(3)運(yùn)行時(shí):切面在運(yùn)行的某個(gè)時(shí)刻被織入,SpringAOP就是以這種方式織入切面的亮曹,原理應(yīng)該是使用了JDK的動(dòng)態(tài)代理技術(shù)。

三秘症、使用AOP的方式

1.經(jīng)典的基于代理的AOP

2.@AspectJ注解驅(qū)動(dòng)的切面

3.純POJO切面

4.注入式AspectJ切面

四照卦、如何實(shí)現(xiàn)AOP

?1.代理方式實(shí)現(xiàn)AOP

(1)可睡覺(jué)的接口,任何可以睡覺(jué)的人或機(jī)器都可以實(shí)現(xiàn)它乡摹。

? ?public interface Sleepable {

? ? public void sleep();

? ? }

(2)接口實(shí)現(xiàn)類役耕,“Me”可以睡覺(jué),“Me”就實(shí)現(xiàn)可以睡覺(jué)的接口聪廉。

? ?public class Me implements Sleepable{

? ? ? ? ?public void sleep() {

? ? ? ? ?System.out.println("\n睡覺(jué)瞬痘!不休息哪里有力氣學(xué)習(xí)!\n");

}

}

(3)Me關(guān)注于睡覺(jué)的邏輯板熊,但是睡覺(jué)需要其他功能輔助框全,比如睡前脫衣服,起床脫衣服干签,這里開(kāi)始就需要AOP替“Me”完成津辩!解耦!首先需要一個(gè)SleepHelper類。因?yàn)橐粋€(gè)是切入點(diǎn)前執(zhí)行喘沿、一個(gè)是切入點(diǎn)之后執(zhí)行情萤,所以實(shí)現(xiàn)對(duì)應(yīng)接口。

public class SleepHelper implements MethodBeforeAdvice, AfterReturningAdvice {

public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {

System.out.println("睡覺(jué)前要脫衣服摹恨!");

}

public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {

System.out.println("起床后要穿衣服筋岛!");

}

}

(4)最關(guān)鍵的來(lái)了,Spring核心配置文件application.xml配置AOP晒哄。

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

<span style="white-space:pre"> </span>xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

<span style="white-space:pre"> </span>xmlns:aop="http://www.springframework.org/schema/aop"

<span style="white-space:pre"> </span>xsi:schemaLocation="http://www.springframework.org/schema/beans

<span style="white-space:pre"> </span>http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

<span style="white-space:pre"> </span>http://www.springframework.org/schema/aop

<span style="white-space:pre"> </span>http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">

? <!-- 定義被代理者 -->

? <bean id="me" class="com.springAOP.bean.Me"></bean>

? <!-- 定義通知內(nèi)容睁宰,也就是切入點(diǎn)執(zhí)行前后需要做的事情 -->

? <bean id="sleepHelper" class="com.springAOP.bean.SleepHelper"></bean>

? <!-- 定義切入點(diǎn)位置 -->

? <bean id="sleepPointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">

<property name="pattern" value=".*sleep"></property>

? </bean>

? <!-- 使切入點(diǎn)與通知相關(guān)聯(lián),完成切面配置 -->

? <bean id="sleepHelperAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">

<property name="advice" ref="sleepHelper"></property>?

? <property name="pointcut" ref="sleepPointcut"></property>

? </bean>

? <!-- 設(shè)置代理 -->

? <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">

<!-- 代理的對(duì)象寝凌,有睡覺(jué)能力 -->

<property name="target" ref="me"></property>

<!-- 使用切面 -->

<property name="interceptorNames" value="sleepHelperAdvisor"></property>

<!-- 代理接口柒傻,睡覺(jué)接口 -->

<property name="proxyInterfaces" value="com.springAOP.bean.Sleepable"></property>

? </bean>

</beans>

其中:

<beans>是Spring的配置標(biāo)簽,beans里面幾個(gè)重要的屬性:

xmlns:

是默認(rèn)的xml文檔解析格式较木,即spring的beans红符。地址是http://www.springframework.org/schema/beans;通過(guò)設(shè)置這個(gè)屬性伐债,所有在beans里面聲明的屬性预侯,可以直接通過(guò)<>來(lái)使用,比如<bean>等等峰锁。一個(gè)XML文件萎馅,只能聲明一個(gè)默認(rèn)的語(yǔ)義解析的規(guī)范。例如上面的xml中就只有beans一個(gè)是默認(rèn)的虹蒋,其他的都需要通過(guò)特定的標(biāo)簽來(lái)使用糜芳,比如aop,它自己有很多的屬性魄衅,如果要使用峭竣,前面就必須加上aop:xxx才可以。類似的晃虫,如果默認(rèn)的xmlns配置的是aop相關(guān)的語(yǔ)義解析規(guī)范皆撩,那么在xml中就可以直接寫config這種標(biāo)簽了。

xmlns:xsi:

是xml需要遵守的規(guī)范傲茄,通過(guò)URL可以看到毅访,是w3的統(tǒng)一規(guī)范沮榜,后面通過(guò)xsi:schemaLocation來(lái)定位所有的解析文件盘榨。

xmlns:aop:

這個(gè)是重點(diǎn),是我們這里需要使用到的一些語(yǔ)義規(guī)范蟆融,與面向切面AOP相關(guān)草巡。

xmlns:tx:

Spring中與事務(wù)相關(guān)的配置內(nèi)容。

(5)測(cè)試類型酥,Test山憨,其中查乒,通過(guò)AOP代理的方式執(zhí)行Me的sleep()方法,會(huì)把執(zhí)行前郁竟、執(zhí)行后的操作執(zhí)行玛迄,實(shí)現(xiàn)了AOP的效果!

public class Test {

public static void main(String[] args){

@SuppressWarnings("resource")

//如果是web項(xiàng)目棚亩,則使用注釋的代碼加載配置文件蓖议,這里是一般的Java項(xiàng)目,所以使用下面的方式

? ? //ApplicationContext appCtx = new ClassPathXmlApplicationContext("application.xml");

ApplicationContext appCtx = new FileSystemXmlApplicationContext("application.xml");

Sleepable me = (Sleepable)appCtx.getBean("proxy");

me.sleep();

}

}

執(zhí)行結(jié)果:

(6)通過(guò)org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator簡(jiǎn)化配置讥蟆。

將配置文件中設(shè)置代理的代碼去掉勒虾,加上:

<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>

然后,在Test中瘸彤,直接獲取me對(duì)象修然,執(zhí)行sleep方法,就可以實(shí)現(xiàn)同樣的功能质况!

通過(guò)自動(dòng)匹配愕宋,切面會(huì)自動(dòng)匹配符合切入點(diǎn)的bean,會(huì)被自動(dòng)代理结榄,實(shí)現(xiàn)功能掏婶!

————————————————

2.更簡(jiǎn)單的方式,通過(guò)AspectJ提供的注解實(shí)現(xiàn)AOP

1)同樣的例子潭陪,修改后的SleepHelper:

@Aspect

public class SleepHelper{

? ? public SleepHelper(){


? ? }


? ? @Pointcut("execution(* *.sleep())")

? ? public void sleeppoint(){}


? ? @Before("sleeppoint()")

? ? public void beforeSleep(){

? ? ? ? System.out.println("睡覺(jué)前要脫衣服!");

? ? }


? ? @AfterReturning("sleeppoint()")

? ? public void afterSleep(){

? ? ? ? System.out.println("睡醒了要穿衣服雄妥!");

? ? }


}

(2)在方法中,可以加上JoinPoint參數(shù)以進(jìn)行相關(guān)操作依溯,如:

//當(dāng)拋出異常時(shí)被調(diào)用

? ? public void doThrowing(JoinPoint point, Throwable ex)

? ? {

? ? ? ? System.out.println("doThrowing::method "

? ? ? ? ? ? ? ? + point.getTarget().getClass().getName() + "."

? ? ? ? ? ? ? ? + point.getSignature().getName() + " throw exception");

? ? ? ? System.out.println(ex.getMessage());

? ? }

(3)然后修改配置為:

? ? ? ? <aop:aspectj-autoproxy />

<!-- 定義通知內(nèi)容老厌,也就是切入點(diǎn)執(zhí)行前后需要做的事情 -->

<bean id="sleepHelper" class="com.springAOP.bean.SleepHelper"></bean>

<!-- 定義被代理者 -->

<bean id="me" class="com.springAOP.bean.Me"></bean>

(4)最后測(cè)試,一樣的結(jié)果黎炉!

public class Test {

public static void main(String[] args){

@SuppressWarnings("resource")

//如果是web項(xiàng)目枝秤,則使用注釋的代碼加載配置文件,這里是一般的Java項(xiàng)目慷嗜,所以使用下面的方式

? ? //ApplicationContext appCtx = new ClassPathXmlApplicationContext("application.xml");

ApplicationContext appCtx = new FileSystemXmlApplicationContext("application.xml");

Sleepable me = (Sleepable)appCtx.getBean("me");

me.sleep();

}

}

3.使用Spring來(lái)定義純粹的POJO切面(名字很繞口淀弹,其實(shí)就是純粹通過(guò)<aop:fonfig>標(biāo)簽配置,也是一種比較簡(jiǎn)單的方式)

1)修改后的SleepHelper類庆械,很正常的類薇溃,所以這種方式的優(yōu)點(diǎn)就是在代碼中不體現(xiàn)任何AOP相關(guān)配置,純粹使用xml配置缭乘。

public class SleepHelper{

? ? public void beforeSleep(){

? ? ? ? System.out.println("睡覺(jué)前要脫衣服!");

? ? }


? ? public void afterSleep(){

? ? ? ? System.out.println("睡醒了要穿衣服沐序!");

? ? }


}

(2)配置文件:

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"

xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

http://www.springframework.org/schema/aop

http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">

<!-- 定義通知內(nèi)容,也就是切入點(diǎn)執(zhí)行前后需要做的事情 -->

<bean id="sleepHelper" class="com.springAOP.bean.SleepHelper"></bean>

<!-- 定義被代理者 -->

<bean id="me" class="com.springAOP.bean.Me"></bean>

<aop:config>

<aop:aspect ref="sleepHelper">

<aop:before method="beforeSleep" pointcut="execution(* *.sleep(..))" />

<aop:after method="afterSleep" pointcut="execution(* *.sleep(..))" />

</aop:aspect>

</aop:config>

</beans>

(3)配置的另一種寫法

<aop:config>

<aop:aspect ref="sleepHelper">

? ? ? ? ? ? <aop:pointcut id="sleepHelpers" expression="execution(* *.sleep(..))" />

? ? ? ? ? ? <aop:before pointcut-ref="sleepHelpers" method="beforeSleep" />

? ? ? ? ? ? <aop:after pointcut-ref="sleepHelpers" method="afterSleep" />? ? ?

? ? ? ? </aop:aspect>

</aop:config>

五、AspectJ與AOP與CGLIB

AspectJ與AOP

1.spectJ是一套獨(dú)立的面向切面編程的解決方案策幼。

1.1AspectJ 安裝

? ? ? AspectJ 下載地址(http://www.eclipse.org/aspectj/downloads.php)邑时。

1.2下載AspectJ? jar包,然后雙擊安裝特姐。安裝好的目錄結(jié)構(gòu)為:

bin:存放了 aj晶丘、aj5、ajc唐含、ajdoc铣口、ajbrowser 等命令,其中 ajc 命令最常用觉壶,它的作用類似于 javac

doc:存放了 AspectJ 的使用說(shuō)明脑题、參考手冊(cè)、API 文檔等文檔

lib:該路徑下的 4 個(gè) JAR 文件是 AspectJ 的核心類庫(kù)

1.3AspectJ HelloWorld 實(shí)現(xiàn)

業(yè)務(wù)組件SayHelloServicepackagecom.ywsc.fenfenzhong.aspectj.learn;publicclassSayHelloService{publicvoidsay(){System.out.print("Hello? AspectJ");}}需要來(lái)了铜靶,在需要在調(diào)用say()方法之后叔遂,需要記錄日志。那就是通過(guò)AspectJ的后置增強(qiáng)吧争剿。

LogAspect日志記錄組件已艰,實(shí)現(xiàn)對(duì)com.ywsc.fenfenzhong.aspectj.learn.SayHelloService后置增強(qiáng)packagecom.ywsc.fenfenzhong.aspectj.learn;publicaspectLogAspect{pointcutlogPointcut():execution(voidSayHelloService.say());after():logPointcut(){System.out.println("記錄日志 ...");}}

編譯SayHelloService

執(zhí)行命令? ajc-d.SayHelloService.javaLogAspect.java生成SayHelloService.class執(zhí)行命令? ? javaSayHelloService輸出HelloAspectJ記錄日志

ajc.exe 可以理解為 javac.exe 命令,都用于編譯 Java 程序蚕苇,區(qū)別是 ajc.exe 命令可識(shí)別 AspectJ 的語(yǔ)法哩掺;我們可以將 ajc.exe 當(dāng)成一個(gè)增強(qiáng)版的 javac.exe 命令.執(zhí)行ajc命令后的 SayHelloService.class 文件不是由原來(lái)的 SayHelloService.java 文件編譯得到的,該 SayHelloService.class 里新增了打印日志的內(nèi)容——這表明 AspectJ 在編譯時(shí)“自動(dòng)”編譯得到了一個(gè)新類涩笤,這個(gè)新類增強(qiáng)了原有的 SayHelloService.java 類的功能嚼吞,因此 AspectJ 通常被稱為編譯時(shí)增強(qiáng)的 AOP 框架。

與 AspectJ 相對(duì)的還有另外一種 AOP 框架蹬碧,它不需要在編譯時(shí)對(duì)目標(biāo)類進(jìn)行增強(qiáng)舱禽,而是運(yùn)行時(shí)生成目標(biāo)類的代理類,該代理類要么與目標(biāo)類實(shí)現(xiàn)相同的接口恩沽,要么是目標(biāo)類的子類——總之誊稚,代理類的實(shí)例可作為目標(biāo)類的實(shí)例來(lái)使用。一般來(lái)說(shuō)罗心,編譯時(shí)增強(qiáng)的 AOP 框架在性能上更有優(yōu)勢(shì)——因?yàn)檫\(yùn)行時(shí)動(dòng)態(tài)增強(qiáng)的 AOP 框架需要每次運(yùn)行時(shí)都進(jìn)行動(dòng)態(tài)增強(qiáng)里伯。

2.Spring AOP也是對(duì)目標(biāo)類增強(qiáng),生成代理類渤闷。但是與AspectJ的最大區(qū)別在于---Spring AOP的運(yùn)行時(shí)增強(qiáng)疾瓮,而AspectJ是編譯時(shí)增強(qiáng)。

? ? ? 曾經(jīng)以為AspectJ是Spring AOP一部分肤晓,是因?yàn)镾pring AOP使用了AspectJ的Annotation爷贫。使用了Aspect來(lái)定義切面,使用Pointcut來(lái)定義切入點(diǎn),使用Advice來(lái)定義增強(qiáng)處理补憾。雖然使用了Aspect的Annotation漫萄,但是并沒(méi)有使用它的編譯器和織入器。其實(shí)現(xiàn)原理是JDK 動(dòng)態(tài)代理盈匾,在運(yùn)行時(shí)生成代理類腾务。

CGLIB與JDK動(dòng)態(tài)代理

1、JDK動(dòng)態(tài)代理

利用攔截器(攔截器必須實(shí)現(xiàn)InvocationHanlder)加上反射機(jī)制生成一個(gè)實(shí)現(xiàn)代理接口的匿名類削饵,

在調(diào)用具體方法前調(diào)用InvokeHandler來(lái)處理岩瘦。

2、CGLIB動(dòng)態(tài)代理

利用ASM開(kāi)源包窿撬,對(duì)代理對(duì)象類的class文件加載進(jìn)來(lái)启昧,通過(guò)修改其字節(jié)碼生成子類來(lái)處理。

3劈伴、何時(shí)使用JDK還是CGLIB密末?

1)如果目標(biāo)對(duì)象實(shí)現(xiàn)了接口,默認(rèn)情況下會(huì)采用JDK的動(dòng)態(tài)代理實(shí)現(xiàn)AOP跛璧。

2)如果目標(biāo)對(duì)象實(shí)現(xiàn)了接口严里,可以強(qiáng)制使用CGLIB實(shí)現(xiàn)AOP。

3)如果目標(biāo)對(duì)象沒(méi)有實(shí)現(xiàn)了接口追城,必須采用CGLIB庫(kù)刹碾,Spring會(huì)自動(dòng)在JDK動(dòng)態(tài)代理和CGLIB之間轉(zhuǎn)換。

4座柱、如何強(qiáng)制使用CGLIB實(shí)現(xiàn)AOP迷帜?

1)添加CGLIB庫(kù)(aspectjrt-xxx.jar、aspectjweaver-xxx.jar色洞、cglib-nodep-xxx.jar)

2)在Spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class="true"/>

4.4具體實(shí)現(xiàn)

接口:

package com.jpeony.spring.proxy.compare;

/**

* 用戶管理接口(真實(shí)主題和代理主題的共同接口瞬矩,這樣在任何可以使用真實(shí)主題的地方都可以使用代理主題代理。)

* --被代理接口定義

*/

public interface IUserManager {

? ? void addUser(String id, String password);

}

實(shí)現(xiàn)類:

package com.jpeony.spring.proxy.compare;

/**

* 用戶管理接口實(shí)現(xiàn)(被代理的實(shí)現(xiàn)類)

*/

public class UserManagerImpl implements IUserManager {

? ? @Override

? ? public void addUser(String id, String password) {

? ? ? ? System.out.println("======調(diào)用了UserManagerImpl.addUser()方法======");

? ? }

}

JDK代理實(shí)現(xiàn):

package com.jpeony.spring.proxy.compare;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

/**

* JDK動(dòng)態(tài)代理類

*/

public class JDKProxy implements InvocationHandler {

? ? /** 需要代理的目標(biāo)對(duì)象 */

? ? private Object targetObject;

? ? /**

? ? * 將目標(biāo)對(duì)象傳入進(jìn)行代理

? ? */

? ? public Object newProxy(Object targetObject) {

? ? ? ? this.targetObject = targetObject;

? ? ? ? //返回代理對(duì)象

? ? ? ? return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),

? ? ? ? ? ? ? ? targetObject.getClass().getInterfaces(), this);

? ? }

? ? /**

? ? * invoke方法

? ? */

? ? @Override

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

? ? ? ? // 一般我們進(jìn)行邏輯處理的函數(shù)比如這個(gè)地方是模擬檢查權(quán)限

? ? ? ? checkPopedom();

? ? ? ? // 設(shè)置方法的返回值

? ? ? ? Object ret = null;

? ? ? ? // 調(diào)用invoke方法锋玲,ret存儲(chǔ)該方法的返回值

? ? ? ? ret? = method.invoke(targetObject, args);

? ? ? ? return ret;

? ? }

? ? /**

? ? * 模擬檢查權(quán)限的例子

? ? */

? ? private void checkPopedom() {

? ? ? ? System.out.println("======檢查權(quán)限checkPopedom()======");

? ? }

}

CGLIB代理實(shí)現(xiàn):

package com.jpeony.spring.proxy.compare;

import net.sf.cglib.proxy.Enhancer;

import net.sf.cglib.proxy.MethodInterceptor;

import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**

* CGLibProxy動(dòng)態(tài)代理類

*/

public class CGLibProxy implements MethodInterceptor {

? ? /** CGLib需要代理的目標(biāo)對(duì)象 */

? ? private Object targetObject;

? ? public Object createProxyObject(Object obj) {

? ? ? ? this.targetObject = obj;

? ? ? ? Enhancer enhancer = new Enhancer();

? ? ? ? enhancer.setSuperclass(obj.getClass());

? ? ? ? enhancer.setCallback(this);

? ? ? ? Object proxyObj = enhancer.create();

? ? ? ? // 返回代理對(duì)象

? ? ? ? return proxyObj;

? ? }

? ? @Override

? ? public Object intercept(Object proxy, Method method, Object[] args,

? ? ? ? ? ? ? ? ? ? ? ? ? ? MethodProxy methodProxy) throws Throwable {

? ? ? ? Object obj = null;

? ? ? ? // 過(guò)濾方法

? ? ? ? if ("addUser".equals(method.getName())) {

? ? ? ? ? ? // 檢查權(quán)限

? ? ? ? ? ? checkPopedom();

? ? ? ? }

? ? ? ? obj = method.invoke(targetObject, args);

? ? ? ? return obj;

? ? }

? ? private void checkPopedom() {

? ? ? ? System.out.println("======檢查權(quán)限checkPopedom()======");

? ? }

}

客戶端測(cè)試類:

package com.jpeony.spring.proxy.compare;

/**

* 代理模式[[ 客戶端--》代理對(duì)象--》目標(biāo)對(duì)象 ]]

*/

public class Client {

? ? public static void main(String[] args) {

? ? ? ? System.out.println("**********************CGLibProxy**********************");

? ? ? ? CGLibProxy cgLibProxy = new CGLibProxy();

? ? ? ? IUserManager userManager = (IUserManager) cgLibProxy.createProxyObject(new UserManagerImpl());

? ? ? ? userManager.addUser("jpeony", "123456");

? ? ? ? System.out.println("**********************JDKProxy**********************");

? ? ? ? JDKProxy jdkPrpxy = new JDKProxy();

? ? ? ? IUserManager userManagerJDK = (IUserManager) jdkPrpxy.newProxy(new UserManagerImpl());

? ? ? ? userManagerJDK.addUser("jpeony", "123456");

? ? }

}

程序運(yùn)行結(jié)果:


5景用、JDK動(dòng)態(tài)代理和CGLIB字節(jié)碼生成的區(qū)別?

1)JDK動(dòng)態(tài)代理只能對(duì)實(shí)現(xiàn)了接口的類生成代理惭蹂,而不能針對(duì)類伞插。

2)CGLIB是針對(duì)類實(shí)現(xiàn)代理,主要是對(duì)指定的類生成一個(gè)子類盾碗,覆蓋其中的方法媚污,

? ? ?并覆蓋其中方法實(shí)現(xiàn)增強(qiáng),但是因?yàn)椴捎玫氖抢^承廷雅,所以該類或方法最好不要聲明成final耗美,

? ? ?對(duì)于final類或方法京髓,是無(wú)法繼承的。

6商架、CGlib比JDK快堰怨?

1)使用CGLib實(shí)現(xiàn)動(dòng)態(tài)代理,CGLib底層采用ASM字節(jié)碼生成框架蛇摸,使用字節(jié)碼技術(shù)生成代理類备图,

在jdk6之前比使用Java反射效率要高。唯一需要注意的是赶袄,CGLib不能對(duì)聲明為final的方法進(jìn)行代理揽涮,

因?yàn)镃GLib原理是動(dòng)態(tài)生成被代理類的子類。

2)在jdk6饿肺、jdk7蒋困、jdk8逐步對(duì)JDK動(dòng)態(tài)代理優(yōu)化之后,在調(diào)用次數(shù)較少的情況下敬辣,JDK代理效率高于CGLIB代理效率家破,

只有當(dāng)進(jìn)行大量調(diào)用的時(shí)候,jdk6和jdk7比CGLIB代理效率低一點(diǎn)购岗,但是到j(luò)dk8的時(shí)候汰聋,jdk代理效率高于CGLIB代理,

總之喊积,每一次jdk版本升級(jí)烹困,jdk代理效率都得到提升,而CGLIB代理消息確有點(diǎn)跟不上步伐乾吻。

7髓梅、Spring如何選擇用JDK還是CGLIB?

1)當(dāng)Bean實(shí)現(xiàn)接口時(shí)绎签,Spring就會(huì)用JDK的動(dòng)態(tài)代理枯饿。

2)當(dāng)Bean沒(méi)有實(shí)現(xiàn)接口時(shí),Spring使用CGlib是實(shí)現(xiàn)诡必。

3)可以強(qiáng)制使用CGlib(在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>)奢方。

8.總結(jié)JDK動(dòng)態(tài)代理與CGLIB

JDK代理是不需要第三方庫(kù)支持,只需要JDK環(huán)境就可以進(jìn)行代理爸舒,使用條件:

1)實(shí)現(xiàn)InvocationHandler?

2)使用Proxy.newProxyInstance產(chǎn)生代理對(duì)象

3)被代理的對(duì)象必須要實(shí)現(xiàn)接口

CGLib必須依賴于CGLib的類庫(kù)蟋字,但是它需要類來(lái)實(shí)現(xiàn)任何接口代理的是指定的類生成一個(gè)子類,

覆蓋其中的方法扭勉,是一種繼承但是針對(duì)接口編程的環(huán)境下推薦使用JDK的代理


原文鏈接:https://blog.csdn.net/yhl_jxy/java/article/details/80635012

原文鏈接:http://www.reibang.com/p/fe8d1e8bd63e

原文鏈接:https://blog.csdn.net/zhangliangzi/java/article/details/52334964

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末鹊奖,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子涂炎,更是在濱河造成了極大的恐慌忠聚,老刑警劉巖设哗,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異两蟀,居然都是意外死亡网梢,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門垫竞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)澎粟,“玉大人蛀序,你說(shuō)我怎么就攤上這事欢瞪。” “怎么了徐裸?”我有些...
    開(kāi)封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵遣鼓,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我重贺,道長(zhǎng)骑祟,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任气笙,我火速辦了婚禮次企,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘潜圃。我一直安慰自己缸棵,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布谭期。 她就那樣靜靜地躺著堵第,像睡著了一般。 火紅的嫁衣襯著肌膚如雪隧出。 梳的紋絲不亂的頭發(fā)上踏志,一...
    開(kāi)封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音胀瞪,去河邊找鬼针余。 笑死,一個(gè)胖子當(dāng)著我的面吹牛凄诞,可吹牛的內(nèi)容都是我干的涵紊。 我是一名探鬼主播,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼幔摸,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼摸柄!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起既忆,我...
    開(kāi)封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤驱负,失蹤者是張志新(化名)和其女友劉穎嗦玖,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體跃脊,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡宇挫,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了酪术。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片器瘪。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖绘雁,靈堂內(nèi)的尸體忽然破棺而出橡疼,到底是詐尸還是另有隱情,我是刑警寧澤庐舟,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布欣除,位于F島的核電站,受9級(jí)特大地震影響挪略,放射性物質(zhì)發(fā)生泄漏历帚。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一杠娱、第九天 我趴在偏房一處隱蔽的房頂上張望挽牢。 院中可真熱鬧,春花似錦摊求、人聲如沸禽拔。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)奏赘。三九已至,卻和暖如春太惠,著一層夾襖步出監(jiān)牢的瞬間磨淌,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工凿渊, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留梁只,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓埃脏,卻偏偏與公主長(zhǎng)得像搪锣,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子彩掐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345