javascript
【SpringBoot】SpingBoot整合AOP
說起spring,我們知道其最核心的兩個功能就是AOP(面向切面)和IOC(控制反轉),這邊文章來總結一下SpringBoot如何整合使用AOP。
一、示例應用場景:對所有的web請求做切面來記錄日志。
1、pom中引入SpringBoot的web模塊和使用AOP相關的依賴:
其中:
cglib包是用來動態代理用的,基于類的代理;
aspectjrt和aspectjweaver是與aspectj相關的包,用來支持切面編程的;
aspectjrt包是aspectj的runtime包;
aspectjweaver是aspectj的織入包;
2、實現一個簡單的web請求入口(實現傳入name參數,返回“hello xxx”的功能):
注意:在完成了引入AOP依賴包后,一般來說并不需要去做其他配置。使用過Spring注解配置方式的人會問是否需要在程序主類中增加@EnableAspectJAutoProxy來啟用,實際并不需要。
因為在AOP的默認配置屬性中,spring.aop.auto屬性默認是開啟的,也就是說只要引入了AOP依賴后,默認已經增加了@EnableAspectJAutoProxy。
3、定義切面類,實現web層的日志切面
要想把一個類變成切面類,需要兩步,
① 在類上使用 @Component 注解 把切面類加入到IOC容器中
② 在類上使用 @Aspect 注解 使之成為切面類
以上的切面類通過 @Pointcut定義的切入點為com.example.aop包下的所有函數做切人,通過 @Before實現切入點的前置通知,通過 @AfterReturning記錄請求返回的對象。
訪問http://localhost:8004/hello?name=lmb得到控制臺輸出如下:
詳細代碼參見本人的Github:SpringBoot整合AOP
二、AOP支持的通知
1、前置通知@Before:在某連接點之前執行的通知,除非拋出一個異常,否則這個通知不能阻止連接點之前的執行流程。
注意:這里用到了JoinPoint和RequestContextHolder。
1)、通過JoinPoint可以獲得通知的簽名信息,如目標方法名、目標方法參數信息等;
2)、通過RequestContextHolder來獲取請求信息,Session信息;
2、后置通知@AfterReturning:在某連接點之后執行的通知,通常在一個匹配的方法返回的時候執行(可以在后置通知中綁定返回值)。
/** * 后置返回通知 * 這里需要注意的是: * 如果參數中的第一個參數為JoinPoint,則第二個參數為返回值的信息 * 如果參數中的第一個參數不為JoinPoint,則第一個參數為returning中對應的參數 * returning:限定了只有目標方法返回值與通知方法相應參數類型時才能執行后置返回通知,否則不執行,* 對于returning對應的通知方法參數為Object類型將匹配任何目標返回值 * @param joinPoint * @param keys */ @AfterReturning(value = POINT_CUT,returning = "keys") public void doAfterReturningAdvice1(JoinPoint joinPoint,Object keys){ logger.info("第一個后置返回通知的返回值:"+keys); } @AfterReturning(value = POINT_CUT,returning = "keys",argNames = "keys") public void doAfterReturningAdvice2(String keys){ logger.info("第二個后置返回通知的返回值:"+keys); }3、后置異常通知@AfterThrowing:在方法拋出異常退出時執行的通知。
/** * 后置異常通知 * 定義一個名字,該名字用于匹配通知實現方法的一個參數名,當目標方法拋出異常返回后,將把目標方法拋出的異常傳給通知方法; * throwing:限定了只有目標方法拋出的異常與通知方法相應參數異常類型時才能執行后置異常通知,否則不執行, * 對于throwing對應的通知方法參數為Throwable類型將匹配任何異常。 * @param joinPoint * @param exception */ @AfterThrowing(value = POINT_CUT,throwing = "exception") public void doAfterThrowingAdvice(JoinPoint joinPoint,Throwable exception){ //目標方法名: logger.info(joinPoint.getSignature().getName()); if(exception instanceof NullPointerException){ logger.info("發生了空指針異常!!!!!"); } }4、后置最終通知@After:當某連接點退出時執行的通知(不論是正常返回還是異常退出)。
/** * 后置最終通知(目標方法只要執行完了就會執行后置通知方法) * @param joinPoint */ @After(value = POINT_CUT) public void doAfterAdvice(JoinPoint joinPoint){ logger.info("后置最終通知執行了!!!!"); }5、環繞通知@Around:包圍一個連接點的通知,如方法調用等。這是最強大的一種通知類型。環繞通知可以在方法調用前后完成自定義的行為,它也會選擇是否繼續執行連接點或者直接返回它自己的返回值或拋出異常來結束執行。
環繞通知最強大,也最麻煩,是一個對方法的環繞,具體方法會通過代理傳遞到切面中去,切面中可選擇執行方法與否,執行幾次方法等。環繞通知使用一個代理ProceedingJoinPoint類型的對象來管理目標對象,所以此通知的第一個參數必須是ProceedingJoinPoint類型。在通知體內調用ProceedingJoinPoint的proceed()方法會導致后臺的連接點方法執行。proceed()方法也可能會被調用并且傳入一個Object[]對象,該數組中的值將被作為方法執行時的入參。
/** * 環繞通知: * 環繞通知非常強大,可以決定目標方法是否執行,什么時候執行,執行時是否需要替換方法參數,執行完畢是否需要替換返回值。 * 環繞通知第一個參數必須是org.aspectj.lang.ProceedingJoinPoint類型 */ @Around(value = POINT_CUT) public Object doAroundAdvice(ProceedingJoinPoint proceedingJoinPoint){ logger.info("環繞通知的目標方法名:"+proceedingJoinPoint.getSignature().getName()); try { Object obj = proceedingJoinPoint.proceed(); return obj; } catch (Throwable throwable) { throwable.printStackTrace(); } return null; }6、有時候我們定義切面的時候,切面中需要使用到目標對象的某個參數,如何使切面能得到目標對象的參數呢?可以使用args來綁定。如果在一個args表達式中應該使用類型名字的地方使用一個參數名字,那么當通知執行的時候對象的參數值將會被傳遞進來。
@Before("execution(* findById*(..)) &&" + "args(id,..)")public void twiceAsOld1(Long id){System.err.println ("切面before執行了。。。。id==" + id);}注意:任何通知方法都可以將第一個參數定義為org.aspectj.lang.JoinPoint類型(環繞通知需要定義第一個參數為ProceedingJoinPoint類型,它是 JoinPoint 的一個子類)。JoinPoint接口提供了一系列有用的方法,比如 getArgs()(返回方法參數)、getThis()(返回代理對象)、getTarget()(返回目標)、getSignature()(返回正在被通知的方法相關信息)和 toString()(打印出正在被通知的方法的有用信息)。
三、切入點表達式
定義切入點的時候需要一個包含名字和任意參數的簽名,還有一個切入點表達式,如execution(public * com.example.aop...(..))
切入點表達式的格式:execution([可見性]返回類型[聲明類型].方法名(參數)[異常])
其中[]內的是可選的,其它的還支持通配符的使用:
1) *:匹配所有字符
2) ..:一般用于匹配多個包,多個參數
3) +:表示類及其子類
4)運算符有:&&,||,!
切入點表達式關鍵詞用例:
1)execution:用于匹配子表達式。
//匹配com.cjm.model包及其子包中所有類中的所有方法,返回類型任意,方法參數任意
@Pointcut(“execution(* com.cjm.model...(..))”)
public void before(){}
2)within:用于匹配連接點所在的Java類或者包。
//匹配Person類中的所有方法
@Pointcut(“within(com.cjm.model.Person)”)
public void before(){}
//匹配com.cjm包及其子包中所有類中的所有方法
@Pointcut(“within(com.cjm..*)”)
public void before(){}
3) this:用于向通知方法中傳入代理對象的引用。
@Before(“before() && this(proxy)”)
public void beforeAdvide(JoinPoint point, Object proxy){
//處理邏輯
}
4)target:用于向通知方法中傳入目標對象的引用。
@Before(“before() && target(target)
public void beforeAdvide(JoinPoint point, Object proxy){
//處理邏輯
}
5)args:用于將參數傳入到通知方法中。
@Before(“before() && args(age,username)”)
public void beforeAdvide(JoinPoint point, int age, String username){
//處理邏輯
}
6)@within :用于匹配在類一級使用了參數確定的注解的類,其所有方法都將被匹配。
@Pointcut(“@within(com.cjm.annotation.AdviceAnnotation)”)
- 所有被@AdviceAnnotation標注的類都將匹配
public void before(){}
7)@target :和@within的功能類似,但必須要指定注解接口的保留策略為RUNTIME。
@Pointcut(“@target(com.cjm.annotation.AdviceAnnotation)”)
public void before(){}
8)@args :傳入連接點的對象對應的Java類必須被@args指定的Annotation注解標注。
@Before(“@args(com.cjm.annotation.AdviceAnnotation)”)
public void beforeAdvide(JoinPoint point){
//處理邏輯
}
9)@annotation :匹配連接點被它參數指定的Annotation注解的方法。也就是說,所有被指定注解標注的方法都將匹配。
@Pointcut(“@annotation(com.cjm.annotation.AdviceAnnotation)”)
public void before(){}
10)bean:通過受管Bean的名字來限定連接點所在的Bean。該關鍵詞是Spring2.5新增的。
@Pointcut(“bean(person)”)
public void before(){}
參考資料:https://www.cnblogs.com/lic309/p/4079194.html
超強干貨來襲 云風專訪:近40年碼齡,通宵達旦的技術人生總結
以上是生活随笔為你收集整理的【SpringBoot】SpingBoot整合AOP的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【SpringBoot】在SpringB
- 下一篇: 消息推送的实现方式