最近,我在給很多人做履歷修改和模擬面試的時候,有部分朋友和我回饋Spring AOP的面試題,今天就和大家來問。
Spring 一開始最強大的就是 IOC / AOP 兩大核心功能,我們今天一起來學習 Spring AOP 常見註解和執行順序。
Spring 面試核心點:
IOC、AOP、Bean注入、Bean的生命週期、Bean的循環依賴
首先我們一起來回顧Spring Aop 中常用的幾個註解:
#@Before
前置通知:目標方法之前執行-
@After
後置通知:目標方法之後執行(總是執行) #@AfterReturning
傳回之後通知:執行方法結束先前執行(異常不執行)@AfterThrowing
異常通知:出香異常後執行@Around
環繞通知:環繞目標方法執行
常見問題
1、你一定知道Spring , 那說說Aop 的去全部通知順序, Spring Boot 或Spring Boot 2 對aop 的執行順序影響?
2、說說你在 AOP 中遇到的那些坑?
範例程式碼
#下面我們先快速建立一個spring aop 的demo 程式來一起討論spring aop 中的一些細節。
設定檔
為了方便我直接使用spring-boot 進行快速的項目搭建,大家可以使用idea 的spring-boot 專案快速建立功能,或是去start.spring.io
上面去快速建立spring-boot 應用程式。
因為本人經常手動去網路上貼上一些依賴導致,依賴衝突服務啟動失敗等一些問題。
plugins { id 'org.springframework.boot' version '2.6.3' id 'io.spring.dependency-management' version '1.0.11.RELEASE' id 'java' } group 'io.zhengsh' version '1.0-SNAPSHOT' repositories { mavenCentral() maven { url 'https://repo.spring.io/milestone' } maven { url 'https://repo.spring.io/snapshot' } } dependencies { # 其实这里也可以不增加 web 配置,为了试验简单,大家请忽略 implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-actuator' implementation 'org.springframework.boot:spring-boot-starter-aop' testImplementation 'org.springframework.boot:spring-boot-starter-test' } tasks.named('test') { useJUnitPlatform() }
介面類別
#首先我們需要定義一個介面。我們這裡可以再來回顧一下JDK 的預設代理實現的選擇:
如果目標物件實現了接口,則預設採用JDK動態代理 如果目標物件沒有實現接口,則採用進行動態代理 如果目標物件實現了接口,且強制Cglib,則使用cglib代理
這塊的邏輯在DefaultAopProxyFactory
大家有興趣可以去看看。
public interface CalcService { public int div(int x, int y); }
實作類別
這裡我門就簡單一點做一個除法操作,可以模擬正常也可以很容易的模擬錯誤。
@Service public class CalcServiceImpl implements CalcService { @Override public int div(int x, int y) { int result = x / y; System.out.println("====> CalcServiceImpl 被调用了,我们的计算结果是:" + result); return result; } }
aop 攔截器
#申明一個攔截器我們要為當前物件增加@Aspect 和@Component ,筆者之前也是才踩過這樣的坑,只加了一個。
其实这块我刚开始也不是很理解,但是我看了 Aspect 注解的定义我就清楚了

这里面根本就没有 Bean 的定义。所以我们还是乖乖的加上两个注解。
还有就是如果当测试的时候需要开启Aop 的支持为配置类上增加@EnableAspectJAutoProxy
注解。
其实 Aop 使用就三个步骤:
定义 Aspect 定义切面 定义 Pointcut 就是定义我们切入点 定义具体的通知,比如: @After, @Before 等。
@Aspect @Component public class MyAspect { @Pointcut("execution(* io.zhengsh.spring.service.impl..*.*(..))") public void divPointCut() { } @Before("divPointCut()") public void beforeNotify() { System.out.println("----===>> @Before 我是前置通知"); } @After("divPointCut") public void afterNotify() { System.out.println("----===>> @After 我是后置通知"); } @AfterReturning("divPointCut") public void afterReturningNotify() { System.out.println("----===>> @AfterReturning 我是前置通知"); } @AfterThrowing("divPointCut") public void afterThrowingNotify() { System.out.println("----===>> @AfterThrowing 我是异常通知"); } @Around("divPointCut") public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { Object retVal; System.out.println("----===>> @Around 环绕通知之前 AAA"); retVal = proceedingJoinPoint.proceed(); System.out.println("----===>> @Around 环绕通知之后 BBB"); return retVal; } }
测试类
其实我这个测试类,虽然用了 @Test 注解,但是我这个类更加像一个 main 方法把:如下所示:

执行结论
结果记录:spring 4.x, spring-boot 1.5.9
无法现在依赖,所以无法试验
我直接说一下结论:Spring 4 中环绕通知是在最里面执行的
结果记录:spring 版本5.3.15 springboot 版本2.6.3

多切面的情况
多个切面的情况下,可以通过@Order指定先后顺序,数字越小,优先级越高。如下图所示:

代理失效场景
下面一种场景会导致 aop 代理失效,因为我们在执行 a 方法的时候其实本质是执行 AServer#a
的方法拦截器(MethodInterceptor
)链, 当我们在 a 方法内直接执行b(), 其实本质就相当于 this.b() , 这个时候由执行 a方法是调用到 a 的原始对象相当于是 this 调用,那么会导致 b() 方法的代理失效。这个问题也是我们开发者在开发过程中最常遇到的一个问题。
@Service public class AService { public void a() { System.out.println("...... a"); b(); } public void b() { System.out.println("...... b"); } }
以上是面試官:Spring Aop 常見註解和執行順序的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

禪工作室 13.0.1
強大的PHP整合開發環境

VSCode Windows 64位元 下載
微軟推出的免費、功能強大的一款IDE編輯器

SublimeText3 英文版
推薦:為Win版本,支援程式碼提示!

SublimeText3 Linux新版
SublimeText3 Linux最新版

MantisBT
Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。