最近、履歴書を修正したり、多くの人に模擬面接を行ったりしていたときに、友人が春の AOP 面接の質問についてフィードバックをくれたので、今日質問してみます。
Spring の最初の最も強力な点は、IOC/AOP の 2 つのコア機能です。今日は Spring AOP の一般的なアノテーションと実行シーケンスを一緒に学びましょう。
春のインタビューのコアポイント:
IOC、AOP、Bean インジェクション、Bean ライフサイクル、Bean 循環依存関係
まず第一に、 Spring Aop で一般的に使用されるいくつかのアノテーションを確認してみましょう:
@Before
事前通知: ターゲット メソッドの前に実行 @After
Post notification: 対象メソッドの後に実行 (常に実行) @AfterReturning
Post notification: 実行メソッドの終了前に実行 (例外が発生した場合は実行されません)@AfterThrowing
例外通知: 例外後に実行 @Around
通知周り: ターゲットメソッド実行周り2. AOP で遭遇した落とし穴について教えてください。
Spring AOP デモ プログラムを簡単に構築して、Spring について一緒に議論しましょう。ああ。
便宜上、spring-boot を直接使用して、プロジェクトを構築するには、アイデアの 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 のデフォルトのプロキシ実装の選択を確認できます:
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; } }
其实这块我刚开始也不是很理解,但是我看了 Aspect 注解的定义我就清楚了
这里面根本就没有 Bean 的定义。所以我们还是乖乖的加上两个注解。
还有就是如果当测试的时候需要开启Aop 的支持为配置类上增加@EnableAspectJAutoProxy
注解。
其实 Aop 使用就三个步骤:
@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 中国語 Web サイトの他の関連記事を参照してください。