javascript
Spring(4)——面向切面编程(AOP模块)
Spring AOP 簡(jiǎn)介
如果說(shuō) IoC 是 Spring 的核心,那么面向切面編程就是 Spring 最為重要的功能之一了,在數(shù)據(jù)庫(kù)事務(wù)中切面編程被廣泛使用。
AOP 即 Aspect Oriented Program 面向切面編程
首先,在面向切面編程的思想里面,把功能分為核心業(yè)務(wù)功能,和周邊功能。
- 所謂的核心業(yè)務(wù),比如登陸,增加數(shù)據(jù),刪除數(shù)據(jù)都叫核心業(yè)務(wù)
- 所謂的周邊功能,比如性能統(tǒng)計(jì),日志,事務(wù)管理等等
周邊功能在 Spring 的面向切面編程AOP思想里,即被定義為切面
在面向切面編程AOP的思想里面,核心業(yè)務(wù)功能和切面功能分別獨(dú)立進(jìn)行開(kāi)發(fā),然后把切面功能和核心業(yè)務(wù)功能 "編織" 在一起,這就叫AOP
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ù)性。
AOP 當(dāng)中的概念:
- 切入點(diǎn)(Pointcut)
在哪些類,哪些方法上切入(where) - 通知(Advice)
在方法執(zhí)行的什么實(shí)際(when:方法前/方法后/方法前后)做什么(what:增強(qiáng)的功能) - 切面(Aspect)
切面 = 切入點(diǎn) + 通知,通俗點(diǎn)就是:在什么時(shí)機(jī),什么地方,做什么增強(qiáng)! - 織入(Weaving)
把切面加入到對(duì)象,并創(chuàng)建出代理對(duì)象的過(guò)程。(由 Spring 來(lái)完成)
一個(gè)例子
為了更好的說(shuō)明 AOP 的概念,我們來(lái)舉一個(gè)實(shí)際中的例子來(lái)說(shuō)明:
在上面的例子中,包租婆的核心業(yè)務(wù)就是簽合同,收房租,那么這就夠了,灰色框起來(lái)的部分都是重復(fù)且邊緣的事,交給中介商就好了,這就是?AOP 的一個(gè)思想:讓關(guān)注點(diǎn)代碼與業(yè)務(wù)代碼分離!
實(shí)際的代碼
我們來(lái)實(shí)際的用代碼感受一下
1.在 Package【pojo】下新建一個(gè)【Landlord】類(我百度翻譯的包租婆的英文):
package pojo;import org.springframework.stereotype.Component;@Component("landlord") public class Landlord {public void service() {// 僅僅只是實(shí)現(xiàn)了核心的業(yè)務(wù)功能System.out.println("簽合同");System.out.println("收房租");} }2.在 Package【aspect】下新建一個(gè)中介商【Broker】類(我還是用的翻譯...):
package aspect;import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component;@Component @Aspect class Broker {@Before("execution(* pojo.Landlord.service())")public void before(){System.out.println("帶租客看房");System.out.println("談價(jià)格");}@After("execution(* pojo.Landlord.service())")public void after(){System.out.println("交鑰匙");} }3.在 applicationContext.xml 中配置自動(dòng)注入,并告訴 Spring IoC 容器去哪里掃描這兩個(gè) Bean:
<?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:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"><context:component-scan base-package="aspect" /><context:component-scan base-package="pojo" /><aop:aspectj-autoproxy/> </beans>4.在 Package【test】下編寫(xiě)測(cè)試代碼:
package test;import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import pojo.Landlord;public class TestSpring {public static void main(String[] args) {ApplicationContext context =new ClassPathXmlApplicationContext("applicationContext.xml");Landlord landlord = (Landlord) context.getBean("landlord", Landlord.class);landlord.service();} }5.執(zhí)行看到效果:
這個(gè)例子使用了一些注解,現(xiàn)在看不懂沒(méi)有關(guān)系,但我們可以從上面可以看到,我們?cè)?Landlord 的 service() 方法中僅僅實(shí)現(xiàn)了核心的業(yè)務(wù)代碼,其余的關(guān)注點(diǎn)功能是根據(jù)我們?cè)O(shè)置的切面自動(dòng)補(bǔ)全的。
使用注解來(lái)開(kāi)發(fā) Spring AOP
使用注解的方式已經(jīng)逐漸成為了主流,所以我們利用上面的例子來(lái)說(shuō)明如何用注解來(lái)開(kāi)發(fā) Spring AOP
第一步:選擇連接點(diǎn)
Spring 是方法級(jí)別的 AOP 框架,我們主要也是以某個(gè)類額某個(gè)方法作為連接點(diǎn),另一種說(shuō)法就是:選擇哪一個(gè)類的哪一方法用以增強(qiáng)功能。
....public void service() {// 僅僅只是實(shí)現(xiàn)了核心的業(yè)務(wù)功能System.out.println("簽合同");System.out.println("收房租");}....我們?cè)谶@里就選擇上述 Landlord 類中的 service() 方法作為連接點(diǎn)。
第二步:創(chuàng)建切面
選擇好了連接點(diǎn)就可以創(chuàng)建切面了,我們可以把切面理解為一個(gè)攔截器,當(dāng)程序運(yùn)行到連接點(diǎn)的時(shí)候,被攔截下來(lái),在開(kāi)頭加入了初始化的方法,在結(jié)尾也加入了銷毀的方法而已,在 Spring 中只要使用?@Aspect?注解一個(gè)類,那么 Spring IoC 容器就會(huì)認(rèn)為這是一個(gè)切面了:
package aspect;import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component;@Component @Aspect class Broker {@Before("execution(* pojo.Landlord.service())")public void before(){System.out.println("帶租客看房");System.out.println("談價(jià)格");}@After("execution(* pojo.Landlord.service())")public void after(){System.out.println("交鑰匙");} }- 注意:?被定義為切面的類仍然是一個(gè) Bean ,需要?@Component?注解標(biāo)注
代碼部分中在方法上面的注解看名字也能猜出個(gè)大概,下面來(lái)列舉一下 Spring 中的 AspectJ 注解:
| @Before | 前置通知,在連接點(diǎn)方法前調(diào)用 |
| @Around | 環(huán)繞通知,它將覆蓋原有方法,但是允許你通過(guò)反射調(diào)用原有方法,后面會(huì)講 |
| @After | 后置通知,在連接點(diǎn)方法后調(diào)用 |
| @AfterReturning | 返回通知,在連接點(diǎn)方法執(zhí)行并正常返回后調(diào)用,要求連接點(diǎn)方法在執(zhí)行過(guò)程中沒(méi)有發(fā)生異常 |
| @AfterThrowing | 異常通知,當(dāng)連接點(diǎn)方法異常時(shí)調(diào)用 |
有了上表,我們就知道 before() 方法是連接點(diǎn)方法調(diào)用前調(diào)用的方法,而 after() 方法則相反,這些注解中間使用了定義切點(diǎn)的正則式,也就是告訴 Spring AOP 需要攔截什么對(duì)象的什么方法,下面講到。
第三步:定義切點(diǎn)
在上面的注解中定義了 execution 的正則表達(dá)式,Spring 通過(guò)這個(gè)正則表達(dá)式判斷具體要攔截的是哪一個(gè)類的哪一個(gè)方法:
execution(* pojo.Landlord.service())依次對(duì)這個(gè)表達(dá)式作出分析:
- execution:代表執(zhí)行方法的時(shí)候會(huì)觸發(fā)
- *?:代表任意返回類型的方法
- pojo.Landlord:代表類的全限定名
- service():被攔截的方法名稱
通過(guò)上面的表達(dá)式,Spring 就會(huì)知道應(yīng)該攔截 pojo.Lnadlord 類下的 service() 方法。上面的演示類還好,如果多出都需要寫(xiě)這樣的表達(dá)式難免會(huì)有些復(fù)雜,我們可以通過(guò)使用?@Pointcut?注解來(lái)定義一個(gè)切點(diǎn)來(lái)避免這樣的麻煩:
package aspect;import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component;@Component @Aspect class Broker {@Pointcut("execution(* pojo.Landlord.service())")public void lService() {}@Before("lService()")public void before() {System.out.println("帶租客看房");System.out.println("談價(jià)格");}@After("lService()")public void after() {System.out.println("交鑰匙");} }第四步:測(cè)試 AOP
編寫(xiě)測(cè)試代碼,但是我這里因?yàn)?JDK 版本不兼容出現(xiàn)了 BUG....(尷尬...)
這就告訴我們:環(huán)境配置很重要...不然莫名其妙的 BUG 讓你崩潰...
環(huán)繞通知
我們來(lái)探討一下環(huán)繞通知,這是 Spring AOP 中最強(qiáng)大的通知,因?yàn)樗闪饲爸猛ㄖ秃笾猛ㄖ?#xff0c;它保留了連接點(diǎn)原有的方法的功能,所以它及強(qiáng)大又靈活,讓我們來(lái)看看:
package aspect;import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.stereotype.Component;@Component @Aspect class Broker {// 注釋掉之前的 @Before 和 @After 注解以及對(duì)應(yīng)的方法 // @Before("execution(* pojo.Landlord.service())") // public void before() { // System.out.println("帶租客看房"); // System.out.println("談價(jià)格"); // } // // @After("execution(* pojo.Landlord.service())") // public void after() { // System.out.println("交鑰匙"); // }// 使用 @Around 注解來(lái)同時(shí)完成前置和后置通知@Around("execution(* pojo.Landlord.service())")public void around(ProceedingJoinPoint joinPoint) {System.out.println("帶租客看房");System.out.println("談價(jià)格");try {joinPoint.proceed();} catch (Throwable throwable) {throwable.printStackTrace();}System.out.println("交鑰匙");} }運(yùn)行測(cè)試代碼,結(jié)果仍然正確:
使用 XML 配置開(kāi)發(fā) Spring AOP
注解是很強(qiáng)大的東西,但基于 XML 的開(kāi)發(fā)我們?nèi)匀恍枰私?#xff0c;我們先來(lái)了解一下 AOP 中可以配置的元素:
| aop:advisor | 定義 AOP 的通知其 | 一種很古老的方式,很少使用 |
| aop:aspect | 定義一個(gè)切面 | —— |
| aop:before | 定義前置通知 | —— |
| aop:after | 定義后置通知 | —— |
| aop:around | 定義環(huán)繞通知 | —— |
| aop:after-returning | 定義返回通知 | —— |
| aop:after-throwing | 定義異常通知 | —— |
| aop:config | 頂層的 AOP 配置元素 | AOP 的配置是以它為開(kāi)始的 |
| aop:declare-parents | 給通知引入新的額外接口,增強(qiáng)功能 | —— |
| aop:pointcut | 定義切點(diǎn) | —— |
有了之前通過(guò)注解來(lái)編寫(xiě)的經(jīng)驗(yàn),并且有了上面的表,我們將上面的例子改寫(xiě)成 XML 配置很容易(去掉所有的注解):
<!-- 裝配 Bean--> <bean name="landlord" class="pojo.Landlord"/> <bean id="broker" class="aspect.Broker"/><!-- 配置AOP --> <aop:config><!-- where:在哪些地方(包.類.方法)做增加 --><aop:pointcut id="landlordPoint"expression="execution(* pojo.Landlord.service())"/><!-- what:做什么增強(qiáng) --><aop:aspect id="logAspect" ref="broker"><!-- when:在什么時(shí)機(jī)(方法前/后/前后) --><aop:around pointcut-ref="landlordPoint" method="around"/></aop:aspect> </aop:config>運(yùn)行測(cè)試程序,看到正確結(jié)果:
擴(kuò)展閱讀:Spring【AOP模塊】就這么簡(jiǎn)單?、?關(guān)于 Spring AOP(AspectJ)你該知曉的一切(慎獨(dú)讀,有些深度...)
參考資料:
- 《Java EE 互聯(lián)網(wǎng)輕量級(jí)框架整合開(kāi)發(fā)》
- 《Java 實(shí)戰(zhàn)(第四版)》
- 萬(wàn)能的百度 and 萬(wàn)能的大腦
歡迎轉(zhuǎn)載,轉(zhuǎn)載請(qǐng)注明出處!
簡(jiǎn)書(shū)ID:@我沒(méi)有三顆心臟
總結(jié)
以上是生活随笔為你收集整理的Spring(4)——面向切面编程(AOP模块)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Spring(3)——装配 Spring
- 下一篇: gradle idea java ssm