1. 程式人生 > >05 Spring Aop例項(AOP 如此簡單)@Aspect、@Around 註解方式配置

05 Spring Aop例項(AOP 如此簡單)@Aspect、@Around 註解方式配置

轉載請註明來源 賴賴的部落格

導語

沒有什麼是不可以改變的,換個角度看世界,截然不同!

IoC相關的基本內容告一段落,本次介紹Spring的第二個特性,AOP,面向切面程式設計,術語聽起來比較不容易理解,沒關係,一切盡在例項中,讓我們看一個簡單的例項,就能明白。

例項

專案工程目錄結構和程式碼獲取地址

獲取地址(版本Log將會註明每一個版本對應的課程)

目錄結構

目錄結構

執行工程

執行具有Main函式的 App.java
得到如下輸出

method start time:1480223298250
userHello
method end time:1480223299250

專案詳解

從App.java入手

App.java

package me.laiyijie.demo;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import me.laiyijie.demo.service.HelloInterface;

public class App {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("root-context.xml");

        HelloInterface userService = context.getBean(HelloInterface.class);

        userService.sayHello();

        context.close();
    }
}

呼叫的是HelloInterfacesayHello方法

HelloInterface.java

package me.laiyijie.demo.service;

public interface HelloInterface{

    void sayHello();

}  

其實現類為UserServiceImpl.java

UserServiceImpl.java

package me.laiyijie.demo.service;

import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements HelloInterface {

    public void sayHello() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("userHello");
    }

}  

誒?情況跟我們看到的程式碼有出入?
sayHello 應該只輸出 userHello,前後兩行輸出從何出現?
在Main函式中找不到一點兒線索!

這就是AOP的一個強大特性:

無侵入性,不改變原有的程式碼,卻能增加功能!

那麼究竟是如何增加功能的呢?

讓我們看看TimeMonitor.java

TimeMonitor.java

package me.laiyijie.demo.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Service;

@Service
@Aspect
public class TimeMonitor {

    @Around("execution(* me.laiyijie.demo.service.UserServiceImpl.sayHello(..))")
    public void monitorAround(ProceedingJoinPoint pjp) throws Throwable {

        System.out.println("method start time:" + System.currentTimeMillis());

        Object re = pjp.proceed();

        System.out.println("method end time:" + System.currentTimeMillis());

    }
}

終於看到了 method start time:1480223298250method end time:1480223299250這兩行輸出是從哪兒出現的了!

讓我們來仔細解讀一下這個類

  • 類有兩個註釋,分別是@Service和@Aspect,第一個註解是使得TimeMonitor受Spring託管並例項化。@Aspect就是使得這個類具有AOP功能(你可以這樣理解)兩個註解缺一不可

類裡面只有一個方法,名字叫做monitorAroud,其實就是為了檢測函式執行時間的!

那麼關鍵點來了,兩個輸出語句是怎麼插入到sayHello方法的前後的呢!

看這個註解:

@Around(“execution(* me.laiyijie.demo.service.UserServiceImpl.sayHello(..))”)

  • @Around表示包圍一個函式,也就是可以在函式執行前做一些事情,也可以在函式執行後做一些事情
  • execution(* me.laiyijie.demo.service.UserServiceImpl.sayHello(..)) 這個比較好理解,就是使用表示式的方式指定了要對哪個函式進行包圍!(除了execution以外還有很多,可以搜尋AspectJ語法來學習)

也就是說,這個註解完整的說明了,應該在函式的什麼位置插入變化,也就是所謂的切點

之後是函式的定義:

public Object monitorAround(ProceedingJoinPoint pjp)

這裡引入了ProceedingJoinPoint,在使用了@Around之後可以帶入這個引數,代表的其實就是sayHello這個函式,不過做了一些封裝
Object re = pjp.proceed(); 就是相當於執行了 sayHello方法!

剩下的程式碼就不用過多解釋了,就是在執行這個函式的前後分別進行了系統時間的獲取。

我們把這個函式體,也就是定義了要做那些事情的程式碼,稱作增強

而包含切點增強結合起來就稱作切面
面向切面由此而來!

Spring AOP 開啟需要的配置

需要配置兩項

  1. pom.xml增加依賴(因為要用到AOP還需要不同的JAR包)
  2. root-context.xml中增加切面相關配置
root-context.xml
<?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/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">

    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    <context:component-scan base-package="me.laiyijie.demo"></context:component-scan>

</beans>

root-context.xml 增加了兩行

  1. xmlns:aop="http://www.springframework.org/schema/aop" 代表加入名稱空間
  2. <aop:aspectj-autoproxy></aop:aspectj-autoproxy> 使用1中引入的aop名稱空間開起自動代理(自動代理具體含義後續慢慢解釋,簡單的理解就是AOP的實現是依靠自動代理實現的)
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>me.laiyijie</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <dependencies>

        <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.3.2.RELEASE</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.9</version>
        </dependency>

    </dependencies>
</project>

增加了一個依賴

  • AspectJ 一個強大的AOP框架,也就是@Aspect和@Around以及ProceedingJoinPoint這些註解和方法的提供者

小結

  1. 增強:定義了應該怎麼把額外的動作加入到指定函式中
  2. 切點:定義了你應該把增強插入到哪個函式的什麼位置
  3. 切面:切點增強組合起來的稱呼

盡情享受AOP吧!給你一個不同尋常的體驗