spring中基於注解使用AOP


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


一個場景

比如用戶登錄,每個請求發起之前都會判斷用戶是否登錄,如果每個請求都去判斷一次,那就重復地做了很多事情,只要是有重復的地方,就有優化的空間。現在就把重復的地方抽取出來,暫且稱之為 " 攔截器 ",然后每次請求之前就先經過" 攔截器 ",這個編程的思想就可以稱之為面向切面編程。AOP(Aspect Oriented Program)

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

spring中使用AOP(基於注解)

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

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

@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");
    }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM