spring中基於註解使用AOP

本文內容:spring中如何使用註解實現面向切面編程,以及如何使用自定義註解。html


一個場景

好比用戶登陸,每一個請求發起以前都會判斷用戶是否登陸,若是每一個請求都去判斷一次,那就重複地作了不少事情,只要是有重複的地方,就有優化的空間。如今就把重複的地方抽取出來,暫且稱之爲 " 攔截器 ",而後每次請求以前就先通過" 攔截器 ",這個編程的思想就能夠稱之爲面向切面編程。AOP(Aspect Oriented Program)java

最典型的應用就是事務管理和權限驗證,還有日誌統計,下文中的案例就是接口執行時間的統計。spring

spring中使用AOP(基於註解)

不得不說註解是個很巧妙的設計,使用不多量的信息描述數據,這類數據稱之爲元數據,描述數據的數據。關於註解的理解,這裏有個傳送門:http://www.importnew.com/10294.html編程

下面的案例是在springBoot中進行的,直觀地感覺一下如何使用註解完成AOP。springboot

@Service
public class UserService {

    public void getUser() {
        //To do something
        System.out.println("getUser() has been called");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

切面是這樣定義的:性能

@Component
@Aspect
public class LoggerAspect {
    /**
     * getUser()執行以前執行
     */
    @Before("execution(* com.springboot.demo.service.UserService.getUser(..))")
    public void callBefore() {
        System.out.println("before call method");
        System.out.println("begin........................");
    }

    /**
     * getUser()執行以後執行
     */
    @After("execution(* com.springboot.demo.service.UserService.getUser(..))")
    public void callAfter() {
        System.out.println("after call method");
        System.out.println("end..............................");
    }
}

來個單元測試驗證一下:單元測試

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserServiceTest {

    @Autowired
    private UserService userService;

    @Test
    public void getUserTest() {
        userService.getUser();
    }
}

具體案例

假若有如下的業務場景: UserService業務類中有個getUser()這個方法,如今想統計一下這個方法的執行時間,可能須要測試這個接口的性能。一般作法是方法開始時獲取系統當前時間,而後方法結束時獲取當前時間,最後 excuteTime=endTime-startTime。測試

若是如今不只是這個方法須要統計,還有getUserByName()、getUserById()須要統計,上述的方法明顯很笨了。優化

使用AOP怎麼解決? 抽取公共部分爲一個切面,方法執行前記錄時間,而後執行目標方法,最後,目標方法執行完成以後再獲取一次系統時間。設計

具體實現以下:在LoggerAspect中再寫一個方法,記錄getUser()方法的執行時間。

/**
     * 記錄執行時間
     * @param point 切點
     * @return
     * @throws Throwable
     */
    @Around("execution(* com.springboot.demo.service.UserService.getUser(..))")
    public Object getMethodExecuteTime(ProceedingJoinPoint point) throws Throwable {
        System.out.println("---------------getMethodExecuteTime------------------");
        long startTime = System.currentTimeMillis();
        //調用目標方法
        Object result = point.proceed();
        long endTime = System.currentTimeMillis();
        long executeTime = endTime - startTime;
        System.out.println("executeTime=" + executeTime + "------------------");

        return result;
    }

@Around將目標方法再次封裝,控制了它的調用時機,以此來記錄getUser()的執行時間。可是好像並無達到記錄UserService中的多個方法的執行時間的目的。

@Around("execution(* com.springboot.demo.service.UserService.getUser(..))")

其中指定了切點是getUser()這個方法,這裏的表達式很豐富,能夠設置爲:

* com.springboot.demo.service.UserService.*(..)

表示UserService中的每個方法都是切點,甚至能夠是這樣:

* com.springboot.demo.service..(..)

表示service包下的全部類的全部方法都是切點,可是這樣很明顯不夠靈活,若是能自定義地控制就更好了。

自定義一個註解

若是用一個註解標註某個方法須要記錄其執行時間,豈不是更加優雅。

/**
 * @Description 標註某個方法須要記錄執行時間
 * @Author YaoQi
 * @Date 2018/7/6 15:51
 */

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented

public @interface Logger {
    String value() default "";
}

註解是用來描述數據的,上面的這個註解的意思是:這個註解將做用於方法,而且在運行時有效。可是這樣只是標註了,如何讀取這個標註的信息?

在LoggerAspect中加入這一個方法:

/**
     * @param point
     * @return
     * @throws Throwable
     */
    @Around("@annotation(com.springboot.demo.annotation.Logger)")
    public Object getMethodExecuteTimeForLogger(ProceedingJoinPoint point) throws Throwable {
        System.out.println("---------------getMethodExecuteTime------------------");
        long startTime = System.currentTimeMillis();
        Object result = point.proceed();
        long endTime = System.currentTimeMillis();
        long executeTime = endTime - startTime;
        System.out.println("executeTime=" + executeTime + "------------------");

        return result;
    }

哪一個方法須要記錄執行時間就將@Logger放在對應的方法上:

@Logger
    public void getUser() {
        System.out.println("getUser() has been called");
    }
相關文章
相關標籤/搜索