你敢信?面試官竟然讓我現(xiàn)場搭建一個AOP測試環(huán)境!
作者個人研發(fā)的在高并發(fā)場景下,提供的簡單、穩(wěn)定、可擴展的延遲消息隊列框架,具有精準的定時任務(wù)和延遲隊列處理功能。自開源半年多以來,已成功為十幾家中小型企業(yè)提供了精準定時調(diào)度方案,經(jīng)受住了生產(chǎn)環(huán)境的考驗。為使更多童鞋受益,現(xiàn)給出開源框架地址:https://github.com/sunshinelyz/mykit-delay
寫在前面
金九銀十的跳槽黃金期已拉開序幕,相信很多小伙伴也在摩拳擦掌,想換一個新的工作環(huán)境。然而,由于今年疫情的影響,很多企業(yè)對于招聘的要求是越來越嚴格。之前,很多不被問及的知識點,最近面試時都會被問到了。這不,有些面試官竟然讓面試者現(xiàn)場搭建一個AOP測試環(huán)境。那怎么辦呢?那就給他搭建一個唄!
什么是AOP?
AOP (Aspect Orient Programming),直譯過來就是 面向切面編程。AOP 是一種編程思想,是面向?qū)ο缶幊?OOP)的一種補充。面向?qū)ο缶幊虒⒊绦虺橄蟪筛鱾€層次的對象,而面向切面編程是將程序抽象成各個切面。
比如,在《Spring實戰(zhàn)(第4版)》中有如下一張圖描述了AOP的大體模型。
從這張圖中,我們可以看出:所謂切面,相當于應(yīng)用對象間的橫切點,我們可以將其單獨抽象為單獨的模塊。
總之一句話:AOP是指在程序的運行期間動態(tài)的將某段代碼切入到指定方法、指定位置進行運行的編程方式。AOP的底層是使用動態(tài)代理實現(xiàn)的。
搭建環(huán)境
1.導入AOP依賴
要想搭建AOP環(huán)境,首先,我們就需要在項目的pom.xml文件中引入AOP的依賴,如下所示。
- <properties>
- <spring.version>5.2.6.RELEASE</spring.version>
- </properties>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-aspects</artifactId>
- <version>${spring.version}</version>
- </dependency>
2.定義目標類
在io.mykit.spring.plugins.register.aop包下創(chuàng)建一個MathHandler類,用于處理數(shù)學計算上的一些邏輯。比如,我們在MathHandler類中定義了一個加法操作,返回兩個整數(shù)類型值的和,如下所示。
- package io.mykit.spring.plugins.register.aop;
- /**
- * @author binghe
- * @version 1.0.0
- * @description 定義一個數(shù)據(jù)處理器類,用于測試AOP
- */
- public class MathHandler {
- public int add(int i, int j){
- System.out.println("目標方法執(zhí)行");
- return i + j;
- }
- }
3.定義切面類
在io.mykit.spring.plugins.register.aspect包下創(chuàng)建一個LogAspect切面類,在LogAspect類中定義了幾個打印日志的方法,以這些方法來感知MathHandler類中的add()方法的運行情況。如果需要切面類來感知目標類方法的運行情況,則需要使用Spring AOP中的通知方法。
AOP中的通知方法及其注解與含義如下:
- 前置通知(@Before):在目標方法運行之前運行。
- 后置通知(@After):在目標方法運行結(jié)束之后運行,不管是正常結(jié)束還是異常結(jié)束都會執(zhí)行。
- 返回通知(@AfterReturning):在目標方法正常返回之后運行。
- 異常通知(@AfterThrowing):在目標方法拋出異常后運行。
- 環(huán)繞通知(@Around):動態(tài)代理,手動推進目標方法運行。
綜上,LogAspect類中的具體方法定義如下所示。
- package io.mykit.spring.plugins.register.aspect;
- import org.aspectj.lang.annotation.*;
- /**
- * @author binghe
- * @version 1.0.0
- * @description 打印日志的切面類
- */
- @Aspect
- public class LogAspect {
- @Pointcut("execution(public int io.mykit.spring.plugins.register.aop.MathHandler.*(..))")
- public void pointCut(){
- }
- @Before("pointCut()")
- public void logStart(){
- System.out.println("加法運行開始,參數(shù)列表是:{}");
- }
- @After("pointCut()")
- public void logEnd(){
- System.out.println("加法運行結(jié)束");
- }
- @AfterReturning("pointCut()")
- public void logReturn(){
- System.out.println("加法正常返回,運行結(jié)果:{}");
- }
- @AfterThrowing("pointCut()")
- public void logException(){
- System.out.println("加法異常,異常信息:{}");
- }
- }
- logStart()方法:MathHandler類的add()方法運行之前運行。
- logEnd()方法:MathHandler類的add()方法運行結(jié)束之后運行。
- logReturn()方法:MathHandler類的add()方法正常返回之后運行。
- logException()方法:MathHandler類的add()方法拋出異常后執(zhí)行。
4.將目標類和切面類加入到IOC容器
在io.mykit.spring.plugins.register.config包中,新建AopConfig類,并使用@Configuration注解標注這是一個Spring的配置類,同時使用@EnableAspectJAutoProxy注解開啟基于注解的AOP模式。在AopConfig類中,使用@Bean注解將MathHandler類和LogAspect類加入到IOC容器中,如下所示。
- package io.mykit.spring.plugins.register.config;
- import io.mykit.spring.plugins.register.aop.MathHandler;
- import io.mykit.spring.plugins.register.aspect.LogAspect;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.context.annotation.EnableAspectJAutoProxy;
- /**
- * @author binghe
- * @version 1.0.0
- * @description 測試AOP
- */
- @Configuration
- @EnableAspectJAutoProxy
- public class AopConfig {
- @Bean
- public MathHandler mathHandler(){
- return new MathHandler();
- }
- @Bean
- public LogAspect logAspect(){
- return new LogAspect();
- }
- }
5.創(chuàng)建測試類
在 io.mykit.spring.test包中創(chuàng)建AopTest測試類,并在AopTest類中創(chuàng)建testAop01()方法,如下所示。
- package io.mykit.spring.test;
- import io.mykit.spring.plugins.register.aop.MathHandler;
- import io.mykit.spring.plugins.register.config.AopConfig;
- import org.junit.Test;
- import org.springframework.context.annotation.AnnotationConfigApplicationContext;
- /**
- * @author binghe
- * @version 1.0.0
- * @description 測試切面
- */
- public class AopTest {
- @Test
- public void testAop01(){
- AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AopConfig.class);
- MathHandler mathHandler = context.getBean(MathHandler.class);
- mathHandler.add(1, 2);
- context.close();
- }
- }
運行AopTest類中的testAop01()方法,輸出的結(jié)果信息如下所示。
- 加法運行開始,參數(shù)列表是:{}
- 目標方法執(zhí)行
- 加法運行結(jié)束
- 加法正常返回,運行結(jié)果:{}
可以看到,執(zhí)行了切面類中的方法,并打印出了相關(guān)信息。但是沒有打印參數(shù)列表和運行結(jié)果。
6.在切面類中打印參數(shù)列表和返回結(jié)果
那如果需要打印出參數(shù)列表和運行結(jié)果,該怎么辦呢?別急,我們繼續(xù)往下看。
要想打印出參數(shù)列表和運行結(jié)果,就需要對LogAspect類中的方法進行優(yōu)化,優(yōu)化后的結(jié)果如下所示。
- package io.mykit.spring.plugins.register.aspect;
- import org.aspectj.lang.JoinPoint;
- import org.aspectj.lang.annotation.*;
- import java.util.Arrays;
- /**
- * @author binghe
- * @version 1.0.0
- * @description 打印日志的切面類
- */
- @Aspect
- public class LogAspect {
- @Pointcut("execution(public int io.mykit.spring.plugins.register.aop.MathHandler.*(..))")
- public void pointCut(){
- }
- @Before("pointCut()")
- public void logStart(JoinPoint joinPoint){
- System.out.println(joinPoint.getSignature().getName() + " 運行開始,參數(shù)列表是:{"+ Arrays.asList(joinPoint.getArgs()) +"}");
- }
- @After("pointCut()")
- public void logEnd(JoinPoint joinPoint){
- System.out.println(joinPoint.getSignature().getName() + " 運行結(jié)束");
- }
- @AfterReturning(value = "pointCut()", returning = "result")
- public void logReturn(JoinPoint joinPoint, Object result){
- System.out.println(joinPoint.getSignature().getName() + " 正常返回,運行結(jié)果:{"+result+"}");
- }
- @AfterThrowing(value = "pointCut()", throwing = "exception")
- public void logException(JoinPoint joinPoint, Exception exception){
- System.out.println(joinPoint.getSignature().getName() + " 異常,異常信息:{"+exception+"}");
- }
- }
這里,需要注意的是:JoinPoint參數(shù)一定要放在參數(shù)的第一位。
此時,我們再次運行AopTest類中的testAop01()方法,輸出的結(jié)果信息如下所示。
- add 運行開始,參數(shù)列表是:{[1, 2]}
- 目標方法執(zhí)行
- add 運行結(jié)束
- add 正常返回,運行結(jié)果:{3}
7.目標方法拋出異常
我們在MathHandler類的add()方法中拋出一個異常,來測試下異常情況,如下所示。
- package io.mykit.spring.plugins.register.aop;
- /**
- * @author binghe
- * @version 1.0.0
- * @description 定義一個數(shù)據(jù)處理器類,用于測試AOP
- */
- public class MathHandler {
- public int add(int i, int j){
- System.out.println("目標方法執(zhí)行");
- throw new RuntimeException();
- //return i + j;
- }
- }
此時,我們再次運行AopTest類中的testAop01()方法,輸出的結(jié)果信息如下所示。
- add 運行開始,參數(shù)列表是:{[1, 2]}
- 目標方法執(zhí)行
- add 運行結(jié)束
- add 異常,異常信息:{java.lang.RuntimeException}
可以看到,正確的輸出了切面中打印的信息。
至此,我們的AOP測試環(huán)境就搭建成功了。
本文轉(zhuǎn)載自微信公眾號「冰河技術(shù)」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系冰河技術(shù)公眾號。