天天看點

溫故知新——Spring AOP(二)

上一篇我們介紹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配置是不一樣的,這是為什麼呢?咱們下一篇再聊吧。