當(dāng)前位置:
首頁(yè) >
面向切面(AOP)之Spring接口方式 schema配置方式 aspectj注解方式
發(fā)布時(shí)間:2025/6/15
45
豆豆
生活随笔
收集整理的這篇文章主要介紹了
面向切面(AOP)之Spring接口方式 schema配置方式 aspectj注解方式
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
一、初識(shí)AOP
??關(guān)于AOP的學(xué)習(xí)可以參看幫助文檔:spring-3.2.0.M2\docs\reference\html目錄下index.html的相關(guān)章節(jié)??
?? 1、AOP:Aspect-OrientedProgramming。AOP是OOP的補(bǔ)充,是GOF的延續(xù)。說(shuō)到AOP,我們就不得不來(lái)提一下軟件的縱向和橫向問(wèn)題。從縱向結(jié)構(gòu)來(lái)看就是我們軟件系統(tǒng)的各個(gè)模塊,它主要負(fù)責(zé)處理我們的核心業(yè)務(wù)(例如商品訂購(gòu)、購(gòu)物車(chē)查看);而從橫向結(jié)構(gòu)來(lái)看,我們幾乎每個(gè)系統(tǒng)又包含一些公共模塊(例如權(quán)限、日志模塊等)。這些公共模塊分布于我們各個(gè)核心業(yè)務(wù)之中(例如訂購(gòu)和查看商品明細(xì)的過(guò)程都需要檢查用戶權(quán)限、記錄系統(tǒng)日志等)。這樣一來(lái)不僅在開(kāi)發(fā)過(guò)程中要處處關(guān)注公共模塊的處理而且開(kāi)發(fā)后維護(hù)起來(lái)也是十分麻煩。而有了AOP之后將應(yīng)用程序中的商業(yè)邏輯同對(duì)其提供支持的通用服務(wù)進(jìn)行分離,使得開(kāi)發(fā)人員可以更多的關(guān)注核心業(yè)務(wù)開(kāi)發(fā)。
?2、AOP術(shù)語(yǔ)
? ? ?切面(aspect):用來(lái)切插業(yè)務(wù)方法的類。
連接點(diǎn)(joinpoint):是切面類和業(yè)務(wù)類的連接點(diǎn),其實(shí)就是封裝了業(yè)務(wù)方法的一些基本屬性,作為通知的參數(shù)來(lái)解析。
通知(advice):在切面類中,聲明對(duì)業(yè)務(wù)方法做額外處理的方法。
切入點(diǎn)(pointcut):業(yè)務(wù)類中指定的方法,作為切面切入的點(diǎn)。其實(shí)就是指定某個(gè)方法作為切面切的地方。
目標(biāo)對(duì)象(target object):被代理對(duì)象。
AOP代理(aop proxy):代理對(duì)象。
通知:
前置通知(before advice):在切入點(diǎn)之前執(zhí)行。
后置通知(after returning advice):在切入點(diǎn)執(zhí)行完成后,執(zhí)行通知。
環(huán)繞通知(around advice):包圍切入點(diǎn),調(diào)用方法前后完成自定義行為。
異常通知(after throwing advice):在切入點(diǎn)拋出異常后,執(zhí)行通知。
??AOP是基于代理模式,了解了jdk動(dòng)態(tài)代理和cglib的用法,對(duì)我們學(xué)習(xí)大有裨益。
二、Spring AOP環(huán)境
要在項(xiàng)目中使用Spring AOP 則需要在項(xiàng)目中導(dǎo)入除了springjar包之外,還有aspectjrt.jar,aspectjweaver.jar,aopalliance.jar,spring-aop-3.2.0.M2.jar和cglib.jar 。
???好了,前提工作準(zhǔn)備完成,Spring提供了很多的實(shí)現(xiàn)AOP的方式:Spring接口方式,schema配置方式和注解等,好了廢話不多說(shuō)了,開(kāi)始springaop學(xué)習(xí)之旅,這篇先以Spring接口的方式學(xué)起!
三、Spring接口方式實(shí)現(xiàn)AOP步驟
?
??利用Spring AOP接口實(shí)現(xiàn)AOP,主要是為了指定自定義通知來(lái)供spring AOP機(jī)制識(shí)別。主要接口:前置通知MethodBeforeAdvice,后置通知:AfterReturningAdvice,環(huán)繞通知:MethodInterceptor,異常通知:ThrowsAdvice。見(jiàn)例子代碼:
?
步驟一、業(yè)務(wù)接口的編寫(xiě)
?//代理類接口,也是業(yè)務(wù)類接口<br>
?// 利用接口的方式,spring aop 將默認(rèn)通過(guò)jdk動(dòng)態(tài)代理來(lái)實(shí)現(xiàn)代理類<br>
?// 不利用接口,則spring aop 將通過(guò)cglib 來(lái)實(shí)現(xiàn)代理類
public interface IBaseBusiness {
?? // 用作代理的切入點(diǎn)方法
?????public String delete(String obj)
?
???//這方法不被切面切
????public String add(String obj);
??
???//這方法切不切呢?可以設(shè)置
???public String modify(String obj);
}
步驟二、業(yè)務(wù)類:
?//業(yè)務(wù)類,也是目標(biāo)對(duì)象
public class BaseBusiness implements IBaseBusiness {
??? //切入點(diǎn)
?????public String delete(String obj) {
???????System.out.println("==========調(diào)用切入點(diǎn):" + obj +"說(shuō):你敢刪除我!===========\n");
???????return obj + ":瞄~";
??? }
??? publicString add(String obj) {
???????System.out.println("================這個(gè)方法不能被切。。。==============\n");
???????return obj + ":瞄~ 嘿嘿!";
??? }
??? publicString modify(String obj) {
???????System.out.println("=================這個(gè)也設(shè)置加入切吧====================\n");
???????return obj + ":瞄改瞄啊!";
??? }
}
步驟三、通知類:
???1、前置通知:
public class BaseBeforeAdvice implements MethodBeforeAdvice {
????// method : 切入的方法 <br>
????//args :切入方法的參數(shù) <br>
????// target :目標(biāo)對(duì)象
???
???@Override
??? public voidbefore(Method method, Object[] args, Object target) throwsThrowable {
???????System.out.println("===========進(jìn)入beforeAdvice()============\n");
???????System.out.print("準(zhǔn)備在" + target + "對(duì)象上用");
???????System.out.print(method + "方法進(jìn)行對(duì) '");
???????System.out.print(args[0] + "'進(jìn)行刪除!\n\n");
???????System.out.println("要進(jìn)入切入點(diǎn)方法了 \n");
??? }
}
?2、后置通知:
public class BaseAfterReturnAdvice implements AfterReturningAdvice{
????//returnValue :切入點(diǎn)執(zhí)行完方法的返回值,但不能修改<br>
????// method :切入點(diǎn)方法 <br>
??? // args:切入點(diǎn)方法的參數(shù)數(shù)組 <br>
????// target :目標(biāo)對(duì)象
????
???@Override
??? public voidafterReturning(Object returnValue, Method method, Object[] args,Object target) throws Throwable {
???????System.out.println("==========進(jìn)入afterReturning()===========\n");
???????System.out.println("切入點(diǎn)方法執(zhí)行完了 \n");
???????System.out.print(args[0] + "在");
???????System.out.print(target + "對(duì)象上被");
???????System.out.print(method + "方法刪除了");
???????System.out.print("只留下:" + returnValue + "\n\n");
??? }
}
? 3、環(huán)繞通知:
public class BaseAroundAdvice implements MethodInterceptor {
????// invocation :連接點(diǎn)
????
???@Override
??? publicObject invoke(MethodInvocation invocation) throws Throwable {
???????System.out.println("===========進(jìn)入around環(huán)繞方法!=========== \n");
???????// 調(diào)用目標(biāo)方法之前執(zhí)行的動(dòng)作
???????System.out.println("調(diào)用方法之前: 執(zhí)行!\n");
???????// 調(diào)用方法的參數(shù)
???????Object[] args = invocation.getArguments();
???????// 調(diào)用的方法
???????Method method = invocation.getMethod();
???????// 獲取目標(biāo)對(duì)象
???????Object target = invocation.getThis();
???????// 執(zhí)行完方法的返回值:調(diào)用proceed()方法,就會(huì)觸發(fā)切入點(diǎn)方法執(zhí)行
???????Object returnValue = invocation.proceed();
???????System.out.println("===========結(jié)束進(jìn)入around環(huán)繞方法!===========\n");
??????
???????System.out.println("輸出:" + args[0] + ";" + method + ";" + target +";" + returnValue + "\n");
???????System.out.println("調(diào)用方法結(jié)束:之后執(zhí)行!\n");
???????return returnValue;
??? }
}
?4、異常通知:
// 異常通知,接口沒(méi)有包含任何方法。通知方法自定義
public class BaseAfterThrowsAdvice implements ThrowsAdvice {
????// 通知方法,需要按照這種格式書(shū)寫(xiě)
????// @param method
????//?????????可選:切入的方法
????//@param args
????//?????????可選:切入的方法的參數(shù)
??? // @paramtarget
????//????????可選:目標(biāo)對(duì)象
????// @param throwable
????//? 必填 : 異常子類,出現(xiàn)這個(gè)異常類的子類,則會(huì)進(jìn)入這個(gè)通知。
???
??? public voidafterThrowing(Method method, Object[] args, Object target,Throwable throwable) {
???????System.out.println("刪除出錯(cuò)啦");
??? }
}
步驟四、定義指定切點(diǎn):
?//定義一個(gè)切點(diǎn),指定對(duì)應(yīng)方法匹配。來(lái)供切面來(lái)針對(duì)方法進(jìn)行處理<br>
?// 繼承NameMatchMethodPointcut類,來(lái)用方法名匹配
public class Pointcut extends NameMatchMethodPointcut {
??? privatestatic final long serialVersionUID = 3990456017285944475L;
???@SuppressWarnings("rawtypes")
???@Override
??? publicboolean matches(Method method, Class targetClass) {
???????// 設(shè)置單個(gè)方法匹配
???????this.setMappedName("delete");
???????// 設(shè)置多個(gè)方法匹配
???????String[] methods = { "delete", "modify" };
??????
???????//也可以用“ * ” 來(lái)做匹配符號(hào)
???????// this.setMappedName("get*");
??????
???????this.setMappedNames(methods);
???????return super.matches(method, targetClass);
??? }
}
步驟五、配置xml文件:
<?xml version="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
???xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:p="http://www.springframework.org/schema/p"
???xmlns:context="http://www.springframework.org/schema/context"
???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??
??????????<!-- 基于注解導(dǎo)入的-->???????
? ? ????http://www.springframework.org/schema/context???
?????????http://www.springframework.org/schema/context/spring-context-3.0.xsd
???????<!-- 基于aop導(dǎo)入的-->
????????http://www.springframework.org/schema/aop???
?????????http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"
???default->
???<!--==============================利用spring自己的aop配置================================-->
???<!-- 聲明一個(gè)業(yè)務(wù)類 -->
???<bean id="baseBusiness"class="aop.base.BaseBusiness" />
??
???<!-- 聲明通知類 -->
???<bean id="baseBefore"class="aop.base.advice.BaseBeforeAdvice" />
???<bean id="baseAfterReturn"class="aop.base.advice.BaseAfterReturnAdvice"/>
???<bean id="baseAfterThrows"class="aop.base.advice.BaseAfterThrowsAdvice"/>
???<bean id="baseAround"class="aop.base.advice.BaseAroundAdvice" />
???<!-- 指定切點(diǎn)匹配類 -->
???<bean id="pointcut"class="aop.base.pointcut.Pointcut" />
???<!-- 包裝通知,指定切點(diǎn) -->
???<bean id="matchBeforeAdvisor"class="org.springframework.aop.support.DefaultPointcutAdvisor">
???????<property name="pointcut">
???????????<ref bean="pointcut" />
???????</property>
???????<property name="advice">
???????????<ref bean="baseBefore" />
???????</property>
???</bean>
???<!-- 使用ProxyFactoryBean 產(chǎn)生代理對(duì)象-->
???<bean id="businessProxy"class="org.springframework.aop.framework.ProxyFactoryBean">
???????<!-- 代理對(duì)象所實(shí)現(xiàn)的接口 ,如果有接口可以這樣設(shè)置-->
???????<propertyname="proxyInterfaces">
???????????<value>aop.base.IBaseBusiness</value>
???????</property>
???????<!-- 設(shè)置目標(biāo)對(duì)象 -->
???????<property name="target">
???????????<ref local="baseBusiness" />
???????</property>
???????<!-- 代理對(duì)象所使用的攔截器 -->
???????<propertyname="interceptorNames">
???????????<list>
???????????????<value>matchBeforeAdvisor</value>
???????????????<value>baseAfterReturn</value>
???????????????<value>baseAround</value>
???????????</list>
???????</property>
???</bean>
</beans>
步驟六、測(cè)試類:
public class Debug {
??? publicstatic void main(String[] args) {
???????ApplicationContext context = newClassPathXmlApplicationContext("aop/schema_aop.xml");
???????AspectBusiness business = (AspectBusiness)context.getBean("aspectBusiness");
???????business.delete("貓");
??? }
}
g、測(cè)試結(jié)果:運(yùn)行下測(cè)試類,清晰明了。由于結(jié)果呈現(xiàn)太長(zhǎng)就不貼了。
具體的代碼實(shí)現(xiàn)可以從代碼注釋中很容易理解接口方式的實(shí)現(xiàn)。結(jié)果也可想而知,前置方法會(huì)在切入點(diǎn)方法之前執(zhí)行,后置會(huì)在切入點(diǎn)方法執(zhí)行之后執(zhí)行,環(huán)繞則會(huì)在切入點(diǎn)方法執(zhí)行前執(zhí)行同事方法結(jié)束也會(huì)執(zhí)行對(duì)應(yīng)的部分。主要是調(diào)用proceed()方法來(lái)執(zhí)行切入點(diǎn)方法。來(lái)作為環(huán)繞通知前后方法的分水嶺。然后在實(shí)現(xiàn)的過(guò)程中,有幾點(diǎn)卻是可以細(xì)揣摩一下的。
可以看出在xml 配置businessProxy這個(gè)bean的時(shí)候,ProxyFactoryBean類中指定了,proxyInterfaces參數(shù)。這里我把他配置了IBaseBusiness接口。因?yàn)樵陧?xiàng)目開(kāi)發(fā)過(guò)程中,往往業(yè)務(wù)類都會(huì)有對(duì)應(yīng)的接口,以方便利用IOC解耦。但SpringAOP卻也能支持沒(méi)有接口的代理。這就是為什么需要導(dǎo)入cglib.jar的包了。看過(guò)spring的源碼,知道在目標(biāo)切入對(duì)象如果有實(shí)現(xiàn)接口,spring會(huì)默認(rèn)使用jdk動(dòng)態(tài)代理來(lái)實(shí)現(xiàn)代理類。如果沒(méi)有接口,則會(huì)通過(guò)cglib來(lái)實(shí)現(xiàn)代理類。
這個(gè)業(yè)務(wù)類現(xiàn)在有前置通知,后置通知,環(huán)繞三個(gè)通知同時(shí)作用,可能以及更多的通知進(jìn)行作用。那么這些通知的執(zhí)行順序是怎么樣的?就這個(gè)例子而言,同時(shí)實(shí)現(xiàn)了三個(gè)通知。在例子xml中,則顯示執(zhí)行before通知,然后執(zhí)行around的前處理,執(zhí)行切點(diǎn)方法,再執(zhí)行return處理。最后執(zhí)行around的后處理。經(jīng)過(guò)測(cè)試,知道spring處理順序是按照xml配置順序依次處理通知,以隊(duì)列的方式存放前通知,以壓棧的方式存放后通知。所以是前通知依次執(zhí)行,后通知到切入點(diǎn)執(zhí)行完之后,從棧里在后進(jìn)先出的形式把后通知執(zhí)行。
在實(shí)現(xiàn)過(guò)程中發(fā)現(xiàn)通知執(zhí)行對(duì)應(yīng)目標(biāo)對(duì)象的整個(gè)類中的方法,如何精確到某個(gè)方法,則需要定義一個(gè)切點(diǎn)匹配的方式:spring提供了方法名匹配或正則方式來(lái)匹配。然后通過(guò)DefaultPointcutAdvisor來(lái)包裝通知,指定切點(diǎn)。
?
利用方式一的配置起來(lái),可見(jiàn)代碼還是非常的厚重的,定義一個(gè)切面就要定義一個(gè)切面類,然而切面類中,就一個(gè)通知方法,著實(shí)沒(méi)有必要。所以Spring提供了,依賴aspectj的schema配置和基于aspectj注解方式。這兩種方式非常簡(jiǎn)介方便使用,也是項(xiàng)目中普遍的使用方式。
一、通過(guò)Scheme配置實(shí)現(xiàn)AOP步驟(SpringAOP環(huán)境的環(huán)境與上篇博文Spring接口方式相同)
??步驟一、編寫(xiě)業(yè)務(wù)類:
public class AspectBusiness {
?? //切入點(diǎn)
???public String delete(String obj) {
???????System.out.println("==========調(diào)用切入點(diǎn):" + obj +"說(shuō):你敢刪除我!===========\n");
???????return obj + ":瞄~";
??? }
??? publicString add(String obj) {
???????System.out.println("================這個(gè)方法不能被切。。。==============\n");
???????return obj + ":瞄~ 嘿嘿!";
??? }
??? publicString modify(String obj) {
???????System.out.println("=================這個(gè)也設(shè)置加入切吧====================\n");
???????return obj + ":瞄改瞄啊!";
??? }
}
步驟二、編寫(xiě)切面類:切面類中,包含了所有的通知
public class AspectAdvice {
????//前置通知
??public void doBefore(JoinPoint jp) {
???????System.out.println("===========進(jìn)入before advice============\n");
???????System.out.print("準(zhǔn)備在" + jp.getTarget().getClass() + "對(duì)象上用");
???????System.out.print(jp.getSignature().getName() + "方法進(jìn)行對(duì) '");
???????System.out.print(jp.getArgs()[0] + "'進(jìn)行刪除!\n\n");
???????System.out.println("要進(jìn)入切入點(diǎn)方法了 \n");
??? }
????// 后置通知
????// @param jp
????//??????????連接點(diǎn)
????// @param result
????//????????返回值
????
??? public voiddoAfter(JoinPoint jp, String result) {
???????System.out.println("==========進(jìn)入after advice=========== \n");
???????System.out.println("切入點(diǎn)方法執(zhí)行完了 \n");
???????System.out.print(jp.getArgs()[0] + "在");
???????System.out.print(jp.getTarget().getClass() + "對(duì)象上被");
???????System.out.print(jp.getSignature().getName() + "方法刪除了");
???????System.out.print("只留下:" + result + "\n\n");
??? }
????// 環(huán)繞通知
??? // @parampjp
????//?????????連接點(diǎn)
???
??? publicobjectdoAround(ProceedingJoinPoint pjp) throws Throwable {
???????System.out.println("===========進(jìn)入around環(huán)繞方法!=========== \n");
???????// 調(diào)用目標(biāo)方法之前執(zhí)行的動(dòng)作
???????System.out.println("調(diào)用方法之前: 執(zhí)行!\n");
???????// 調(diào)用方法的參數(shù)
???????Object[] args = pjp.getArgs();
???????// 調(diào)用的方法名
???????String method = pjp.getSignature().getName();
???????// 獲取目標(biāo)對(duì)象
???????Object target = pjp.getTarget();
???????// 執(zhí)行完方法的返回值:調(diào)用proceed()方法,就會(huì)觸發(fā)切入點(diǎn)方法執(zhí)行
???????Object result = pjp.proceed();
???????System.out.println("輸出:" + args[0] + ";" + method + ";" + target +";" + result + "\n");
???????System.out.println("調(diào)用方法結(jié)束:之后執(zhí)行!\n");
????return?result;
??? }
?
????//異常通知
??
??? public voiddoThrow(JoinPoint jp, Throwable e) {
???????System.out.println("刪除出錯(cuò)啦");
??? }
}
步驟四、配置文件的編寫(xiě):
<?xml version="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
???xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:p="http://www.springframework.org/schema/p"
???xmlns:context="http://www.springframework.org/schema/context"
???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/context???
?????????http://www.springframework.org/schema/context/spring-context-3.0.xsd
?????????http://www.springframework.org/schema/aop???
?????????http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"
???default->
???<!-- ==============================利用spring利用aspectj來(lái)配置AOP================================-->
???<!-- 聲明一個(gè)業(yè)務(wù)類 -->
???<bean id="aspectBusiness"class="aop.schema.AspectBusiness" />
??? <!-- 聲明通知類 -->
???<bean id="aspectAdvice"class="aop.schema.advice.AspectAdvice"/>
???<aop:config>
???????<aop:aspect id="businessAspect"ref="aspectAdvice">
???????????<!-- 配置指定切入的對(duì)象 -->
???????????<aop:pointcut id="point_cut" expression="execution(*aop.schema.*.*(..))" />
???????????<!-- 只匹配add方法作為切入點(diǎn)
???????????<aop:pointcut id="except_add"expression="execution(* aop.schema.*.add(..))"/>
????????????-->
???????????<!-- 前置通知 -->
???????????<aop:before method="doBefore"pointcut-ref="point_cut" />
???????????<!-- 后置通知 returning指定返回參數(shù) -->
???????????<aop:after-returning method="doAfter"
???????????????pointcut-ref="point_cut" returning="result" />
???????????<aop:around method="doAround"pointcut-ref="point_cut"/>
???????????<aop:after-throwing method="doThrow"pointcut-ref="point_cut" throwing="e"/>
???????</aop:aspect>
???</aop:config>
</beans>
步驟五、測(cè)試類:
public class Debug {
??? publicstatic void main(String[] args) {
???????ApplicationContext context = newClassPathXmlApplicationContext("aop/schema_aop.xml");
???????AspectBusiness business = (AspectBusiness)context.getBean("aspectBusiness");
???????business.delete("貓");
??? }
}
一、簡(jiǎn)介
?? 1、AOP用在哪些方面:AOP能夠?qū)⒛切┡c業(yè)務(wù)無(wú)關(guān),卻為業(yè)務(wù)模塊所共同調(diào)用的邏輯或責(zé)任,例如事務(wù)處理、日志管理、權(quán)限控制,異常處理等,封裝起來(lái),便于減少系統(tǒng)的重復(fù)代碼,降低模塊間的耦合度,并有利于未來(lái)的可操作性和可維護(hù)性。
?? 2、AOP中的概念:
???Aspect(切面):指橫切性關(guān)注點(diǎn)的抽象即為切面,它與類相似,只是兩者的關(guān)注點(diǎn)不一樣,類是對(duì)物體特征的抽象,而切面是橫切性關(guān)注點(diǎn)的抽象.
?joinpoint(連接點(diǎn)):所謂連接點(diǎn)是指那些被攔截到的點(diǎn)(可以是方法、屬性、或者類的初始化時(shí)機(jī)(可以是Action層、Service層、dao層))。在spring中,這些點(diǎn)指的是方法,因?yàn)閟pring只支持方法類型的連接點(diǎn),實(shí)際上joinpoint還可以是field或類構(gòu)造器)
Pointcut(切入點(diǎn)):所謂切入點(diǎn)是指我們要對(duì)那些joinpoint進(jìn)行攔截的定義,也即joinpoint的集合.
Advice(通知):所謂通知是指攔截到j(luò)oinpoint之后所要做的事情就是通知.通知分為前置通知,后置通知,異常通知,最終通知,環(huán)繞通知
Target(目標(biāo)對(duì)象):代理的目標(biāo)對(duì)象
Weave(織入):指將aspects應(yīng)用到target對(duì)象并導(dǎo)致proxy對(duì)象創(chuàng)建的過(guò)程稱為織入.
Introduction(引入):在不修改類代碼的前提下,Introduction可以在運(yùn)行期為類動(dòng)態(tài)地添加一些方法或Field.
??3、AOP帶來(lái)的好處::降低模塊的耦合度;使系統(tǒng)容易擴(kuò)展;更好的代碼復(fù)用性
二、通過(guò)注解方式實(shí)現(xiàn)Spring的AOP(SpringAOP環(huán)境的環(huán)境與上篇博文
步驟一、編寫(xiě)業(yè)務(wù)類:
@Component???//使用自動(dòng)注解的方式實(shí)例化并初始化該類
public class Business {
?? // 切入點(diǎn)
???public String delete(String obj) {
???????System.out.println("==========調(diào)用切入點(diǎn):" + obj +"說(shuō):你敢刪除我!===========\n");
???????return obj + ":瞄~";
??? }
??? publicString add(String obj) {
???????System.out.println("================這個(gè)方法不能被切。。。==============\n");
???????return obj + ":瞄~ 嘿嘿!";
??? }
??? publicString modify(String obj) {
???????System.out.println("=================這個(gè)也設(shè)置加入切吧====================\n");
???????return obj + ":瞄改瞄啊!";
??? }
}
步驟二、切面類:
?// @Aspect : 標(biāo)記為切面類
?// @Pointcut : 指定匹配切點(diǎn)集合
?// @Before : 指定前置通知,value中指定切入點(diǎn)匹配
?// @AfterReturning :后置通知,具有可以指定返回值
// @AfterThrowing :異常通知
?//注意:前置/后置/異常通知的函數(shù)都沒(méi)有返回值,只有環(huán)繞通知有返回值
@Component???//首先初始化切面類
@Aspect?????//聲明為切面類,底層使用動(dòng)態(tài)代理實(shí)現(xiàn)AOP
public class AspectAdvice {
??? //指定切入點(diǎn)匹配表達(dá)式,注意它是以方法的形式進(jìn)行聲明的。
??? //即切點(diǎn)集合是:aop.annotation包下所有類所有方法
??//第一個(gè)*代表返回值類型
//如果要設(shè)置多個(gè)切點(diǎn)可以使用 || 拼接
?@Pointcut("execution(* aop.annotation.*.*(..))|| execution(*com.action.admin.*.*update*(..))")
??? public voidanyMethod() {
??? }
??? //前置通知
?? //在切點(diǎn)方法集合執(zhí)行前,執(zhí)行前置通知
???@Before("execution(*aop.annotation.*.*(..))")
??? public voiddoBefore(JoinPoint jp) {
???????System.out.println("===========進(jìn)入before advice============\n");
???????System.out.print("準(zhǔn)備在" + jp.getTarget().getClass() + "對(duì)象上用");
???????System.out.print(jp.getSignature().getName() + "方法進(jìn)行對(duì) '");
???????System.out.print(jp.getArgs()[0] + "'進(jìn)行刪除!\n\n");
???????System.out.println("要進(jìn)入切入點(diǎn)方法了 \n");
??? }
?? //后置通知??
?@AfterReturning(value = "anyMethod()",returning = "result")
??? public voiddoAfter(JoinPoint jp, String result) {
???????System.out.println("==========進(jìn)入after advice=========== \n");
???????System.out.println("切入點(diǎn)方法執(zhí)行完了 \n");
???????System.out.print(jp.getArgs()[0] + "在");
???????System.out.print(jp.getTarget().getClass() + "對(duì)象上被");
???????System.out.print(jp.getSignature().getName() + "方法刪除了");
???????System.out.print("只留下:" + result + "\n\n");
??? }
??? // 環(huán)繞通知(##環(huán)繞通知的方法中一定要有ProceedingJoinPoint參數(shù),與
???//Filter中的?doFilter方法類似)
???
???@Around("execution(*aop.annotation.*.*(..))")
??? publicObject doAround(ProceedingJoinPointpjp) throws Throwable {
???????System.out.println("===========進(jìn)入around環(huán)繞方法!=========== \n");
?HttpSession session =ServletActionContext.getRequest().getSession();
????Emp login =(Emp)session.getAttribute("login");//ssh2整合后AOP也可以得到request、response、session等
???????// 調(diào)用目標(biāo)方法之前執(zhí)行的動(dòng)作
???????System.out.println("調(diào)用方法之前: 執(zhí)行!\n");
???????// 調(diào)用方法的參數(shù)
???????Object[] args = pjp.getArgs();
???????// 調(diào)用的方法名
???????String method = pjp.getSignature().getName();
???????// 獲取目標(biāo)對(duì)象(形如:com.action.admin.LoginAction@1a2467a)
???????Object target = pjp.getTarget();
??????//獲取目標(biāo)對(duì)象的類名(形如:com.action.admin.LoginAction)
?????String targetName = pjp.getTarget().getClass().getName();
???????// 執(zhí)行完方法的返回值:調(diào)用proceed()方法,就會(huì)觸發(fā)切入點(diǎn)方法執(zhí)行
???????Object result =pjp.proceed();//result的值就是被攔截方法的返回值
???????System.out.println("輸出:" + args[0] + ";" + method + ";" + target +";" + result + "\n");
???????System.out.println("調(diào)用方法結(jié)束:之后執(zhí)行!\n");
?????return result;
??? }
??? // 異常通知
???
???@AfterThrowing(value= "execution(* aop.annotation.*.*(..))", throwing = "e")
??? public voiddoThrow(JoinPoint jp, Throwable e) {
???????System.out.println("刪除出錯(cuò)啦");
??? }
}
?步驟三、xml配置:
<?xml version="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
???xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:p="http://www.springframework.org/schema/p"
???xmlns:context="http://www.springframework.org/schema/context"
???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/context???
?????????http://www.springframework.org/schema/context/spring-context-3.0.xsd
?????????http://www.springframework.org/schema/aop???
?????????http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"
???default->
???<context:component-scanbase-package="aop.annotation" />
??? <!-- 打開(kāi)aop 注解 -->
???<aop:aspectj-autoproxyproxy-target-class="true"/>
</beans>
步驟四、測(cè)試類:
public class Debug {
??? publicstatic void main(String[] args) {
???????ApplicationContext context = newClassPathXmlApplicationContext("aop/annotation_aop.xml");
???????Business business = (Business) context.getBean("business");
???????business.delete("貓");
??? }
}
??關(guān)于AOP的學(xué)習(xí)可以參看幫助文檔:spring-3.2.0.M2\docs\reference\html目錄下index.html的相關(guān)章節(jié)??
?? 1、AOP:Aspect-OrientedProgramming。AOP是OOP的補(bǔ)充,是GOF的延續(xù)。說(shuō)到AOP,我們就不得不來(lái)提一下軟件的縱向和橫向問(wèn)題。從縱向結(jié)構(gòu)來(lái)看就是我們軟件系統(tǒng)的各個(gè)模塊,它主要負(fù)責(zé)處理我們的核心業(yè)務(wù)(例如商品訂購(gòu)、購(gòu)物車(chē)查看);而從橫向結(jié)構(gòu)來(lái)看,我們幾乎每個(gè)系統(tǒng)又包含一些公共模塊(例如權(quán)限、日志模塊等)。這些公共模塊分布于我們各個(gè)核心業(yè)務(wù)之中(例如訂購(gòu)和查看商品明細(xì)的過(guò)程都需要檢查用戶權(quán)限、記錄系統(tǒng)日志等)。這樣一來(lái)不僅在開(kāi)發(fā)過(guò)程中要處處關(guān)注公共模塊的處理而且開(kāi)發(fā)后維護(hù)起來(lái)也是十分麻煩。而有了AOP之后將應(yīng)用程序中的商業(yè)邏輯同對(duì)其提供支持的通用服務(wù)進(jìn)行分離,使得開(kāi)發(fā)人員可以更多的關(guān)注核心業(yè)務(wù)開(kāi)發(fā)。
?2、AOP術(shù)語(yǔ)
? ? ?切面(aspect):用來(lái)切插業(yè)務(wù)方法的類。
連接點(diǎn)(joinpoint):是切面類和業(yè)務(wù)類的連接點(diǎn),其實(shí)就是封裝了業(yè)務(wù)方法的一些基本屬性,作為通知的參數(shù)來(lái)解析。
通知(advice):在切面類中,聲明對(duì)業(yè)務(wù)方法做額外處理的方法。
切入點(diǎn)(pointcut):業(yè)務(wù)類中指定的方法,作為切面切入的點(diǎn)。其實(shí)就是指定某個(gè)方法作為切面切的地方。
目標(biāo)對(duì)象(target object):被代理對(duì)象。
AOP代理(aop proxy):代理對(duì)象。
通知:
前置通知(before advice):在切入點(diǎn)之前執(zhí)行。
后置通知(after returning advice):在切入點(diǎn)執(zhí)行完成后,執(zhí)行通知。
環(huán)繞通知(around advice):包圍切入點(diǎn),調(diào)用方法前后完成自定義行為。
異常通知(after throwing advice):在切入點(diǎn)拋出異常后,執(zhí)行通知。
??AOP是基于代理模式,了解了jdk動(dòng)態(tài)代理和cglib的用法,對(duì)我們學(xué)習(xí)大有裨益。
二、Spring AOP環(huán)境
要在項(xiàng)目中使用Spring AOP 則需要在項(xiàng)目中導(dǎo)入除了springjar包之外,還有aspectjrt.jar,aspectjweaver.jar,aopalliance.jar,spring-aop-3.2.0.M2.jar和cglib.jar 。
???好了,前提工作準(zhǔn)備完成,Spring提供了很多的實(shí)現(xiàn)AOP的方式:Spring接口方式,schema配置方式和注解等,好了廢話不多說(shuō)了,開(kāi)始springaop學(xué)習(xí)之旅,這篇先以Spring接口的方式學(xué)起!
三、Spring接口方式實(shí)現(xiàn)AOP步驟
?
??利用Spring AOP接口實(shí)現(xiàn)AOP,主要是為了指定自定義通知來(lái)供spring AOP機(jī)制識(shí)別。主要接口:前置通知MethodBeforeAdvice,后置通知:AfterReturningAdvice,環(huán)繞通知:MethodInterceptor,異常通知:ThrowsAdvice。見(jiàn)例子代碼:
?
步驟一、業(yè)務(wù)接口的編寫(xiě)
?//代理類接口,也是業(yè)務(wù)類接口<br>
?// 利用接口的方式,spring aop 將默認(rèn)通過(guò)jdk動(dòng)態(tài)代理來(lái)實(shí)現(xiàn)代理類<br>
?// 不利用接口,則spring aop 將通過(guò)cglib 來(lái)實(shí)現(xiàn)代理類
public interface IBaseBusiness {
?? // 用作代理的切入點(diǎn)方法
?????public String delete(String obj)
?
???//這方法不被切面切
????public String add(String obj);
??
???//這方法切不切呢?可以設(shè)置
???public String modify(String obj);
}
步驟二、業(yè)務(wù)類:
?//業(yè)務(wù)類,也是目標(biāo)對(duì)象
public class BaseBusiness implements IBaseBusiness {
??? //切入點(diǎn)
?????public String delete(String obj) {
???????System.out.println("==========調(diào)用切入點(diǎn):" + obj +"說(shuō):你敢刪除我!===========\n");
???????return obj + ":瞄~";
??? }
??? publicString add(String obj) {
???????System.out.println("================這個(gè)方法不能被切。。。==============\n");
???????return obj + ":瞄~ 嘿嘿!";
??? }
??? publicString modify(String obj) {
???????System.out.println("=================這個(gè)也設(shè)置加入切吧====================\n");
???????return obj + ":瞄改瞄啊!";
??? }
}
步驟三、通知類:
???1、前置通知:
public class BaseBeforeAdvice implements MethodBeforeAdvice {
????// method : 切入的方法 <br>
????//args :切入方法的參數(shù) <br>
????// target :目標(biāo)對(duì)象
???
???@Override
??? public voidbefore(Method method, Object[] args, Object target) throwsThrowable {
???????System.out.println("===========進(jìn)入beforeAdvice()============\n");
???????System.out.print("準(zhǔn)備在" + target + "對(duì)象上用");
???????System.out.print(method + "方法進(jìn)行對(duì) '");
???????System.out.print(args[0] + "'進(jìn)行刪除!\n\n");
???????System.out.println("要進(jìn)入切入點(diǎn)方法了 \n");
??? }
}
?2、后置通知:
public class BaseAfterReturnAdvice implements AfterReturningAdvice{
????//returnValue :切入點(diǎn)執(zhí)行完方法的返回值,但不能修改<br>
????// method :切入點(diǎn)方法 <br>
??? // args:切入點(diǎn)方法的參數(shù)數(shù)組 <br>
????// target :目標(biāo)對(duì)象
????
???@Override
??? public voidafterReturning(Object returnValue, Method method, Object[] args,Object target) throws Throwable {
???????System.out.println("==========進(jìn)入afterReturning()===========\n");
???????System.out.println("切入點(diǎn)方法執(zhí)行完了 \n");
???????System.out.print(args[0] + "在");
???????System.out.print(target + "對(duì)象上被");
???????System.out.print(method + "方法刪除了");
???????System.out.print("只留下:" + returnValue + "\n\n");
??? }
}
? 3、環(huán)繞通知:
public class BaseAroundAdvice implements MethodInterceptor {
????// invocation :連接點(diǎn)
????
???@Override
??? publicObject invoke(MethodInvocation invocation) throws Throwable {
???????System.out.println("===========進(jìn)入around環(huán)繞方法!=========== \n");
???????// 調(diào)用目標(biāo)方法之前執(zhí)行的動(dòng)作
???????System.out.println("調(diào)用方法之前: 執(zhí)行!\n");
???????// 調(diào)用方法的參數(shù)
???????Object[] args = invocation.getArguments();
???????// 調(diào)用的方法
???????Method method = invocation.getMethod();
???????// 獲取目標(biāo)對(duì)象
???????Object target = invocation.getThis();
???????// 執(zhí)行完方法的返回值:調(diào)用proceed()方法,就會(huì)觸發(fā)切入點(diǎn)方法執(zhí)行
???????Object returnValue = invocation.proceed();
???????System.out.println("===========結(jié)束進(jìn)入around環(huán)繞方法!===========\n");
??????
???????System.out.println("輸出:" + args[0] + ";" + method + ";" + target +";" + returnValue + "\n");
???????System.out.println("調(diào)用方法結(jié)束:之后執(zhí)行!\n");
???????return returnValue;
??? }
}
?4、異常通知:
// 異常通知,接口沒(méi)有包含任何方法。通知方法自定義
public class BaseAfterThrowsAdvice implements ThrowsAdvice {
????// 通知方法,需要按照這種格式書(shū)寫(xiě)
????// @param method
????//?????????可選:切入的方法
????//@param args
????//?????????可選:切入的方法的參數(shù)
??? // @paramtarget
????//????????可選:目標(biāo)對(duì)象
????// @param throwable
????//? 必填 : 異常子類,出現(xiàn)這個(gè)異常類的子類,則會(huì)進(jìn)入這個(gè)通知。
???
??? public voidafterThrowing(Method method, Object[] args, Object target,Throwable throwable) {
???????System.out.println("刪除出錯(cuò)啦");
??? }
}
步驟四、定義指定切點(diǎn):
?//定義一個(gè)切點(diǎn),指定對(duì)應(yīng)方法匹配。來(lái)供切面來(lái)針對(duì)方法進(jìn)行處理<br>
?// 繼承NameMatchMethodPointcut類,來(lái)用方法名匹配
public class Pointcut extends NameMatchMethodPointcut {
??? privatestatic final long serialVersionUID = 3990456017285944475L;
???@SuppressWarnings("rawtypes")
???@Override
??? publicboolean matches(Method method, Class targetClass) {
???????// 設(shè)置單個(gè)方法匹配
???????this.setMappedName("delete");
???????// 設(shè)置多個(gè)方法匹配
???????String[] methods = { "delete", "modify" };
??????
???????//也可以用“ * ” 來(lái)做匹配符號(hào)
???????// this.setMappedName("get*");
??????
???????this.setMappedNames(methods);
???????return super.matches(method, targetClass);
??? }
}
步驟五、配置xml文件:
<?xml version="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
???xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:p="http://www.springframework.org/schema/p"
???xmlns:context="http://www.springframework.org/schema/context"
???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??
??????????<!-- 基于注解導(dǎo)入的-->???????
? ? ????http://www.springframework.org/schema/context???
?????????http://www.springframework.org/schema/context/spring-context-3.0.xsd
???????<!-- 基于aop導(dǎo)入的-->
????????http://www.springframework.org/schema/aop???
?????????http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"
???default->
???<!--==============================利用spring自己的aop配置================================-->
???<!-- 聲明一個(gè)業(yè)務(wù)類 -->
???<bean id="baseBusiness"class="aop.base.BaseBusiness" />
??
???<!-- 聲明通知類 -->
???<bean id="baseBefore"class="aop.base.advice.BaseBeforeAdvice" />
???<bean id="baseAfterReturn"class="aop.base.advice.BaseAfterReturnAdvice"/>
???<bean id="baseAfterThrows"class="aop.base.advice.BaseAfterThrowsAdvice"/>
???<bean id="baseAround"class="aop.base.advice.BaseAroundAdvice" />
???<!-- 指定切點(diǎn)匹配類 -->
???<bean id="pointcut"class="aop.base.pointcut.Pointcut" />
???<!-- 包裝通知,指定切點(diǎn) -->
???<bean id="matchBeforeAdvisor"class="org.springframework.aop.support.DefaultPointcutAdvisor">
???????<property name="pointcut">
???????????<ref bean="pointcut" />
???????</property>
???????<property name="advice">
???????????<ref bean="baseBefore" />
???????</property>
???</bean>
???<!-- 使用ProxyFactoryBean 產(chǎn)生代理對(duì)象-->
???<bean id="businessProxy"class="org.springframework.aop.framework.ProxyFactoryBean">
???????<!-- 代理對(duì)象所實(shí)現(xiàn)的接口 ,如果有接口可以這樣設(shè)置-->
???????<propertyname="proxyInterfaces">
???????????<value>aop.base.IBaseBusiness</value>
???????</property>
???????<!-- 設(shè)置目標(biāo)對(duì)象 -->
???????<property name="target">
???????????<ref local="baseBusiness" />
???????</property>
???????<!-- 代理對(duì)象所使用的攔截器 -->
???????<propertyname="interceptorNames">
???????????<list>
???????????????<value>matchBeforeAdvisor</value>
???????????????<value>baseAfterReturn</value>
???????????????<value>baseAround</value>
???????????</list>
???????</property>
???</bean>
</beans>
步驟六、測(cè)試類:
public class Debug {
??? publicstatic void main(String[] args) {
???????ApplicationContext context = newClassPathXmlApplicationContext("aop/schema_aop.xml");
???????AspectBusiness business = (AspectBusiness)context.getBean("aspectBusiness");
???????business.delete("貓");
??? }
}
g、測(cè)試結(jié)果:運(yùn)行下測(cè)試類,清晰明了。由于結(jié)果呈現(xiàn)太長(zhǎng)就不貼了。
具體的代碼實(shí)現(xiàn)可以從代碼注釋中很容易理解接口方式的實(shí)現(xiàn)。結(jié)果也可想而知,前置方法會(huì)在切入點(diǎn)方法之前執(zhí)行,后置會(huì)在切入點(diǎn)方法執(zhí)行之后執(zhí)行,環(huán)繞則會(huì)在切入點(diǎn)方法執(zhí)行前執(zhí)行同事方法結(jié)束也會(huì)執(zhí)行對(duì)應(yīng)的部分。主要是調(diào)用proceed()方法來(lái)執(zhí)行切入點(diǎn)方法。來(lái)作為環(huán)繞通知前后方法的分水嶺。然后在實(shí)現(xiàn)的過(guò)程中,有幾點(diǎn)卻是可以細(xì)揣摩一下的。
可以看出在xml 配置businessProxy這個(gè)bean的時(shí)候,ProxyFactoryBean類中指定了,proxyInterfaces參數(shù)。這里我把他配置了IBaseBusiness接口。因?yàn)樵陧?xiàng)目開(kāi)發(fā)過(guò)程中,往往業(yè)務(wù)類都會(huì)有對(duì)應(yīng)的接口,以方便利用IOC解耦。但SpringAOP卻也能支持沒(méi)有接口的代理。這就是為什么需要導(dǎo)入cglib.jar的包了。看過(guò)spring的源碼,知道在目標(biāo)切入對(duì)象如果有實(shí)現(xiàn)接口,spring會(huì)默認(rèn)使用jdk動(dòng)態(tài)代理來(lái)實(shí)現(xiàn)代理類。如果沒(méi)有接口,則會(huì)通過(guò)cglib來(lái)實(shí)現(xiàn)代理類。
這個(gè)業(yè)務(wù)類現(xiàn)在有前置通知,后置通知,環(huán)繞三個(gè)通知同時(shí)作用,可能以及更多的通知進(jìn)行作用。那么這些通知的執(zhí)行順序是怎么樣的?就這個(gè)例子而言,同時(shí)實(shí)現(xiàn)了三個(gè)通知。在例子xml中,則顯示執(zhí)行before通知,然后執(zhí)行around的前處理,執(zhí)行切點(diǎn)方法,再執(zhí)行return處理。最后執(zhí)行around的后處理。經(jīng)過(guò)測(cè)試,知道spring處理順序是按照xml配置順序依次處理通知,以隊(duì)列的方式存放前通知,以壓棧的方式存放后通知。所以是前通知依次執(zhí)行,后通知到切入點(diǎn)執(zhí)行完之后,從棧里在后進(jìn)先出的形式把后通知執(zhí)行。
在實(shí)現(xiàn)過(guò)程中發(fā)現(xiàn)通知執(zhí)行對(duì)應(yīng)目標(biāo)對(duì)象的整個(gè)類中的方法,如何精確到某個(gè)方法,則需要定義一個(gè)切點(diǎn)匹配的方式:spring提供了方法名匹配或正則方式來(lái)匹配。然后通過(guò)DefaultPointcutAdvisor來(lái)包裝通知,指定切點(diǎn)。
?
利用方式一的配置起來(lái),可見(jiàn)代碼還是非常的厚重的,定義一個(gè)切面就要定義一個(gè)切面類,然而切面類中,就一個(gè)通知方法,著實(shí)沒(méi)有必要。所以Spring提供了,依賴aspectj的schema配置和基于aspectj注解方式。這兩種方式非常簡(jiǎn)介方便使用,也是項(xiàng)目中普遍的使用方式。
一、通過(guò)Scheme配置實(shí)現(xiàn)AOP步驟(SpringAOP環(huán)境的環(huán)境與上篇博文
Spring接口方式相同)
??步驟一、編寫(xiě)業(yè)務(wù)類:
public class AspectBusiness {
?? //切入點(diǎn)
???public String delete(String obj) {
???????System.out.println("==========調(diào)用切入點(diǎn):" + obj +"說(shuō):你敢刪除我!===========\n");
???????return obj + ":瞄~";
??? }
??? publicString add(String obj) {
???????System.out.println("================這個(gè)方法不能被切。。。==============\n");
???????return obj + ":瞄~ 嘿嘿!";
??? }
??? publicString modify(String obj) {
???????System.out.println("=================這個(gè)也設(shè)置加入切吧====================\n");
???????return obj + ":瞄改瞄啊!";
??? }
}
步驟二、編寫(xiě)切面類:切面類中,包含了所有的通知
public class AspectAdvice {
????//前置通知
??public void doBefore(JoinPoint jp) {
???????System.out.println("===========進(jìn)入before advice============\n");
???????System.out.print("準(zhǔn)備在" + jp.getTarget().getClass() + "對(duì)象上用");
???????System.out.print(jp.getSignature().getName() + "方法進(jìn)行對(duì) '");
???????System.out.print(jp.getArgs()[0] + "'進(jìn)行刪除!\n\n");
???????System.out.println("要進(jìn)入切入點(diǎn)方法了 \n");
??? }
????// 后置通知
????// @param jp
????//??????????連接點(diǎn)
????// @param result
????//????????返回值
????
??? public voiddoAfter(JoinPoint jp, String result) {
???????System.out.println("==========進(jìn)入after advice=========== \n");
???????System.out.println("切入點(diǎn)方法執(zhí)行完了 \n");
???????System.out.print(jp.getArgs()[0] + "在");
???????System.out.print(jp.getTarget().getClass() + "對(duì)象上被");
???????System.out.print(jp.getSignature().getName() + "方法刪除了");
???????System.out.print("只留下:" + result + "\n\n");
??? }
????// 環(huán)繞通知
??? // @parampjp
????//?????????連接點(diǎn)
???
??? publicobjectdoAround(ProceedingJoinPoint pjp) throws Throwable {
???????System.out.println("===========進(jìn)入around環(huán)繞方法!=========== \n");
???????// 調(diào)用目標(biāo)方法之前執(zhí)行的動(dòng)作
???????System.out.println("調(diào)用方法之前: 執(zhí)行!\n");
???????// 調(diào)用方法的參數(shù)
???????Object[] args = pjp.getArgs();
???????// 調(diào)用的方法名
???????String method = pjp.getSignature().getName();
???????// 獲取目標(biāo)對(duì)象
???????Object target = pjp.getTarget();
???????// 執(zhí)行完方法的返回值:調(diào)用proceed()方法,就會(huì)觸發(fā)切入點(diǎn)方法執(zhí)行
???????Object result = pjp.proceed();
???????System.out.println("輸出:" + args[0] + ";" + method + ";" + target +";" + result + "\n");
???????System.out.println("調(diào)用方法結(jié)束:之后執(zhí)行!\n");
????return?result;
??? }
?
????//異常通知
??
??? public voiddoThrow(JoinPoint jp, Throwable e) {
???????System.out.println("刪除出錯(cuò)啦");
??? }
}
步驟四、配置文件的編寫(xiě):
<?xml version="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
???xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:p="http://www.springframework.org/schema/p"
???xmlns:context="http://www.springframework.org/schema/context"
???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/context???
?????????http://www.springframework.org/schema/context/spring-context-3.0.xsd
?????????http://www.springframework.org/schema/aop???
?????????http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"
???default->
???<!-- ==============================利用spring利用aspectj來(lái)配置AOP================================-->
???<!-- 聲明一個(gè)業(yè)務(wù)類 -->
???<bean id="aspectBusiness"class="aop.schema.AspectBusiness" />
??? <!-- 聲明通知類 -->
???<bean id="aspectAdvice"class="aop.schema.advice.AspectAdvice"/>
???<aop:config>
???????<aop:aspect id="businessAspect"ref="aspectAdvice">
???????????<!-- 配置指定切入的對(duì)象 -->
???????????<aop:pointcut id="point_cut" expression="execution(*aop.schema.*.*(..))" />
???????????<!-- 只匹配add方法作為切入點(diǎn)
???????????<aop:pointcut id="except_add"expression="execution(* aop.schema.*.add(..))"/>
????????????-->
???????????<!-- 前置通知 -->
???????????<aop:before method="doBefore"pointcut-ref="point_cut" />
???????????<!-- 后置通知 returning指定返回參數(shù) -->
???????????<aop:after-returning method="doAfter"
???????????????pointcut-ref="point_cut" returning="result" />
???????????<aop:around method="doAround"pointcut-ref="point_cut"/>
???????????<aop:after-throwing method="doThrow"pointcut-ref="point_cut" throwing="e"/>
???????</aop:aspect>
???</aop:config>
</beans>
步驟五、測(cè)試類:
public class Debug {
??? publicstatic void main(String[] args) {
???????ApplicationContext context = newClassPathXmlApplicationContext("aop/schema_aop.xml");
???????AspectBusiness business = (AspectBusiness)context.getBean("aspectBusiness");
???????business.delete("貓");
??? }
}
一、簡(jiǎn)介
?? 1、AOP用在哪些方面:AOP能夠?qū)⒛切┡c業(yè)務(wù)無(wú)關(guān),卻為業(yè)務(wù)模塊所共同調(diào)用的邏輯或責(zé)任,例如事務(wù)處理、日志管理、權(quán)限控制,異常處理等,封裝起來(lái),便于減少系統(tǒng)的重復(fù)代碼,降低模塊間的耦合度,并有利于未來(lái)的可操作性和可維護(hù)性。
?? 2、AOP中的概念:
???Aspect(切面):指橫切性關(guān)注點(diǎn)的抽象即為切面,它與類相似,只是兩者的關(guān)注點(diǎn)不一樣,類是對(duì)物體特征的抽象,而切面是橫切性關(guān)注點(diǎn)的抽象.
?joinpoint(連接點(diǎn)):所謂連接點(diǎn)是指那些被攔截到的點(diǎn)(可以是方法、屬性、或者類的初始化時(shí)機(jī)(可以是Action層、Service層、dao層))。在spring中,這些點(diǎn)指的是方法,因?yàn)閟pring只支持方法類型的連接點(diǎn),實(shí)際上joinpoint還可以是field或類構(gòu)造器)
Pointcut(切入點(diǎn)):所謂切入點(diǎn)是指我們要對(duì)那些joinpoint進(jìn)行攔截的定義,也即joinpoint的集合.
Advice(通知):所謂通知是指攔截到j(luò)oinpoint之后所要做的事情就是通知.通知分為前置通知,后置通知,異常通知,最終通知,環(huán)繞通知
Target(目標(biāo)對(duì)象):代理的目標(biāo)對(duì)象
Weave(織入):指將aspects應(yīng)用到target對(duì)象并導(dǎo)致proxy對(duì)象創(chuàng)建的過(guò)程稱為織入.
Introduction(引入):在不修改類代碼的前提下,Introduction可以在運(yùn)行期為類動(dòng)態(tài)地添加一些方法或Field.
??3、AOP帶來(lái)的好處::降低模塊的耦合度;使系統(tǒng)容易擴(kuò)展;更好的代碼復(fù)用性
二、通過(guò)注解方式實(shí)現(xiàn)Spring的AOP(SpringAOP環(huán)境的環(huán)境與上篇博文
Spring接口方式相同)
?注解在項(xiàng)目中已經(jīng)到處都是了,撇開(kāi)一些優(yōu)劣不提,開(kāi)發(fā)的便利性和可讀性是非常的方便的。用來(lái)配置SpringAOP也非常簡(jiǎn)單便利步驟一、編寫(xiě)業(yè)務(wù)類:
@Component???//使用自動(dòng)注解的方式實(shí)例化并初始化該類
public class Business {
?? // 切入點(diǎn)
???public String delete(String obj) {
???????System.out.println("==========調(diào)用切入點(diǎn):" + obj +"說(shuō):你敢刪除我!===========\n");
???????return obj + ":瞄~";
??? }
??? publicString add(String obj) {
???????System.out.println("================這個(gè)方法不能被切。。。==============\n");
???????return obj + ":瞄~ 嘿嘿!";
??? }
??? publicString modify(String obj) {
???????System.out.println("=================這個(gè)也設(shè)置加入切吧====================\n");
???????return obj + ":瞄改瞄啊!";
??? }
}
步驟二、切面類:
?// @Aspect : 標(biāo)記為切面類
?// @Pointcut : 指定匹配切點(diǎn)集合
?// @Before : 指定前置通知,value中指定切入點(diǎn)匹配
?// @AfterReturning :后置通知,具有可以指定返回值
// @AfterThrowing :異常通知
?//注意:前置/后置/異常通知的函數(shù)都沒(méi)有返回值,只有環(huán)繞通知有返回值
@Component???//首先初始化切面類
@Aspect?????//聲明為切面類,底層使用動(dòng)態(tài)代理實(shí)現(xiàn)AOP
public class AspectAdvice {
??? //指定切入點(diǎn)匹配表達(dá)式,注意它是以方法的形式進(jìn)行聲明的。
??? //即切點(diǎn)集合是:aop.annotation包下所有類所有方法
??//第一個(gè)*代表返回值類型
//如果要設(shè)置多個(gè)切點(diǎn)可以使用 || 拼接
?@Pointcut("execution(* aop.annotation.*.*(..))|| execution(*com.action.admin.*.*update*(..))")
??? public voidanyMethod() {
??? }
??? //前置通知
?? //在切點(diǎn)方法集合執(zhí)行前,執(zhí)行前置通知
???@Before("execution(*aop.annotation.*.*(..))")
??? public voiddoBefore(JoinPoint jp) {
???????System.out.println("===========進(jìn)入before advice============\n");
???????System.out.print("準(zhǔn)備在" + jp.getTarget().getClass() + "對(duì)象上用");
???????System.out.print(jp.getSignature().getName() + "方法進(jìn)行對(duì) '");
???????System.out.print(jp.getArgs()[0] + "'進(jìn)行刪除!\n\n");
???????System.out.println("要進(jìn)入切入點(diǎn)方法了 \n");
??? }
?? //后置通知??
?@AfterReturning(value = "anyMethod()",returning = "result")
??? public voiddoAfter(JoinPoint jp, String result) {
???????System.out.println("==========進(jìn)入after advice=========== \n");
???????System.out.println("切入點(diǎn)方法執(zhí)行完了 \n");
???????System.out.print(jp.getArgs()[0] + "在");
???????System.out.print(jp.getTarget().getClass() + "對(duì)象上被");
???????System.out.print(jp.getSignature().getName() + "方法刪除了");
???????System.out.print("只留下:" + result + "\n\n");
??? }
??? // 環(huán)繞通知(##環(huán)繞通知的方法中一定要有ProceedingJoinPoint參數(shù),與
???//Filter中的?doFilter方法類似)
???
???@Around("execution(*aop.annotation.*.*(..))")
??? publicObject doAround(ProceedingJoinPointpjp) throws Throwable {
???????System.out.println("===========進(jìn)入around環(huán)繞方法!=========== \n");
?HttpSession session =ServletActionContext.getRequest().getSession();
????Emp login =(Emp)session.getAttribute("login");//ssh2整合后AOP也可以得到request、response、session等
???????// 調(diào)用目標(biāo)方法之前執(zhí)行的動(dòng)作
???????System.out.println("調(diào)用方法之前: 執(zhí)行!\n");
???????// 調(diào)用方法的參數(shù)
???????Object[] args = pjp.getArgs();
???????// 調(diào)用的方法名
???????String method = pjp.getSignature().getName();
???????// 獲取目標(biāo)對(duì)象(形如:com.action.admin.LoginAction@1a2467a)
???????Object target = pjp.getTarget();
??????//獲取目標(biāo)對(duì)象的類名(形如:com.action.admin.LoginAction)
?????String targetName = pjp.getTarget().getClass().getName();
???????// 執(zhí)行完方法的返回值:調(diào)用proceed()方法,就會(huì)觸發(fā)切入點(diǎn)方法執(zhí)行
???????Object result =pjp.proceed();//result的值就是被攔截方法的返回值
???????System.out.println("輸出:" + args[0] + ";" + method + ";" + target +";" + result + "\n");
???????System.out.println("調(diào)用方法結(jié)束:之后執(zhí)行!\n");
?????return result;
??? }
??? // 異常通知
???
???@AfterThrowing(value= "execution(* aop.annotation.*.*(..))", throwing = "e")
??? public voiddoThrow(JoinPoint jp, Throwable e) {
???????System.out.println("刪除出錯(cuò)啦");
??? }
}
?步驟三、xml配置:
<?xml version="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
???xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:p="http://www.springframework.org/schema/p"
???xmlns:context="http://www.springframework.org/schema/context"
???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/context???
?????????http://www.springframework.org/schema/context/spring-context-3.0.xsd
?????????http://www.springframework.org/schema/aop???
?????????http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"
???default->
???<context:component-scanbase-package="aop.annotation" />
??? <!-- 打開(kāi)aop 注解 -->
???<aop:aspectj-autoproxyproxy-target-class="true"/>
</beans>
步驟四、測(cè)試類:
public class Debug {
??? publicstatic void main(String[] args) {
???????ApplicationContext context = newClassPathXmlApplicationContext("aop/annotation_aop.xml");
???????Business business = (Business) context.getBean("business");
???????business.delete("貓");
??? }
}
總結(jié)
以上是生活随笔為你收集整理的面向切面(AOP)之Spring接口方式 schema配置方式 aspectj注解方式的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Spring的AOP使用xml配置
- 下一篇: Spring学习8-Spring事务管理