本文內容:spring中如何使用註解實現面向切面編程,以及如何使用自定義註解。html
好比用戶登陸,每一個請求發起以前都會判斷用戶是否登陸,若是每一個請求都去判斷一次,那就重複地作了不少事情,只要是有重複的地方,就有優化的空間。如今就把重複的地方抽取出來,暫且稱之爲 " 攔截器 ",而後每次請求以前就先通過" 攔截器 ",這個編程的思想就能夠稱之爲面向切面編程。AOP(Aspect Oriented Program)java
最典型的應用就是事務管理和權限驗證,還有日誌統計,下文中的案例就是接口執行時間的統計。spring
不得不說註解是個很巧妙的設計,使用不多量的信息描述數據,這類數據稱之爲元數據,描述數據的數據。關於註解的理解,這裏有個傳送門: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"); }