溫故知新——Spring AOP(二)

牛初九發表於2020-08-28

上一篇我們介紹Spring AOP的註解的配置,也叫做Java Config。今天我們看看比較傳統的xml的方式如何配置AOP。整體的場景我們還是用原來的,“我穿上跑鞋”,“我要去跑步”。Service層的程式碼我們不變,還是用原來的,如下:

@Service
public class MyService {
    public void gotorun() {
        System.out.println("我要去跑步!");
    }
}

再看看上一篇中的MyAspect程式碼,裡邊都是使用註解配置的,我們AOP相關的配置全部刪除掉,只留下“我床上跑鞋“這樣一個方法,如下:

public class MyAspect {
    public void putonshoes() {
        System.out.println("我穿上跑步鞋。");
    }
}

類中沒有任何的註解,我們將全部通過xml的方式配置AOP。首先,我們要在xml中引入aop的schema,如下:

<?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.xsd
       http://www.springframework.org/schema/aop
       https://www.springframework.org/schema/aop/spring-aop.xsd">

</beans>

有了aop的schema,我們就可以使用Spring的aop的標籤了,我們先將MyAspect例項化,因為我們的通知方法”我穿上跑鞋“在這個類中,如下:

<bean id="myAspect" class="com.example.springaopdemo.aspect.MyAspect" />

其中,id我們配置為myAspect。然後,我們就要配置<aop:config>了,這個標籤說明這是一段aop配置,具體的aop內容都在這個標籤內,如下:

<aop:config proxy-target-class="true">
    ……
</aop:config>

其中,我們還可以配置proxy-target-class這個屬性,還記得這個屬性是什麼意思嗎?對了,它代表著是否使用CGLIB代理,由於我們專案引入的依賴是spring-boot-starter-aop,預設是使用CGLIB的,所以這裡配置不配置都可以。

然後在裡邊我們配置切面<aop:aspect>,它標識著這是一個切面配置,在標籤裡還要指定我們的切面的bean,也就是myAspect,如下:

<aop:aspect id="aopAspect" ref="myAspect">
    ……
</aop:aspect>

切面的id叫做aopAspect,ref指定我們切面的bean,就是前面例項化的myAspect。好了,切面就配置好了,然後就是切點和通知。切點和通知的配置一定要在<aop:aspect>內,說明這個切點和通知屬於當前這個切面的。

先來看看切點<aop:pointcut>的配置吧,如下:

<aop:pointcut id="pointcut" 
     expression="execution(* com.example.springaopdemo.service.*.*(..))">
</aop:pointcut>

是不是很熟悉,我們看到了匹配方法的表示式。同樣,我們要給切點定義一個id叫做pointcut,然後expression就是匹配的表示式,這個和上一篇是一樣的,沒有區別。在這裡,我們還是匹配service包下的所有類的所有方法。好了,到這裡切點就配置完成了。

最後,再來看看通知,通知是和<aop:pointcut>並列的,都在<aop:aspect>內,具體如下:

<aop:before method="putonshoes" pointcut-ref="pointcut"></aop:before>

通知的5種型別,分別對應著5個不同的標籤,在這裡我們還是使用前置通知<aop:before>,在標籤的內部,要指定它對應的切點,pointcut-ref="pointcut",切點我們指定前面配置的,id是pointcut。然後就要指定方法method了,這個方法是哪個類中的方法呢?還記得我們再配置<aop:aspect>時指定的bean嗎?ref指定了myAspect,那麼method指定的方法就是myAspect這個bean中的方法。這裡我們配置putonshoes方法。

好了,到這裡,aop的配置就全部配置完了,我們看一下全貌吧,

<bean id="myAspect" class="com.example.springaopdemo.aspect.MyAspect" />

<aop:config proxy-target-class="true">
    <aop:aspect id="aspect" ref="myAspect">
        <aop:pointcut id="pointcut" 
             expression="execution(* com.example.springaopdemo.service.*.*(..))">
        </aop:pointcut>
        <aop:before method="putonshoes" pointcut-ref="pointcut"></aop:before>
    </aop:aspect>
</aop:config>

最後,我們在SpringBoot的啟動類中,使用@ImportResource("spring-aop.xml") 引入這個xml檔案,如下:

@SpringBootApplication
@ImportResource("spring-aop.xml")
public class SpringAopDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringAopDemoApplication.class, args);
    }
}

測試類的程式和上一篇是一致,沒有變化,如下:

@SpringBootTest
class SpringAopDemoApplicationTests {
    @Autowired
    private MyService myService;

    @Test
    public void testAdvice() {
        myService.gotorun();
    }
}

執行一下看看結果,

我穿上跑步鞋。
我要去跑步!

沒有問題,符合預期。

在上一篇中,我們可以使用簡單的配置,也就是不配置切點,在通知中直接配置匹配表示式,如果忘記的同學可以翻一翻上一篇的內容。在xml的aop配置中,也是可以省略掉切點pointcut的配置的,我們在通知中,直接配置表示式,如下:

<aop:config proxy-target-class="true">
    <aop:aspect id="aspect" ref="myAspect">
        <aop:before method="putonshoes" 
             pointcut="execution(* com.example.springaopdemo.service.*.*(..))">
        </aop:before>
    </aop:aspect>
</aop:config>

是不是比前面的配置看起來清爽一些了。小夥伴們自己執行一下吧,結果是沒有問題的。

好了,Spring AOP的Java Config和Schema-based 兩種的方式的配置都介紹完了。我們擴充一下思維,Spring的事務管理也是AOP吧,在方法執行之前開啟事務,在方法執行後提交事務。但是大家有沒有留意,Spring的事務配置和我們們的AOP配置是不一樣的,這是為什麼呢?我們們下一篇再聊吧。

相關文章