1. 程式人生 > >最簡 Spring AOP 原始碼分析!

最簡 Spring AOP 原始碼分析!

![](https://img2020.cnblogs.com/other/633265/202012/633265-20201210143003352-2147163579.jpg) # 前言 最近在研究 Spring 原始碼,Spring 最核心的功能就是 `IOC 容器`和 `AOP`。本文定位是以最簡的方式,分析 Spring AOP 原始碼。 # 基本概念 ![](https://img2020.cnblogs.com/other/633265/202012/633265-20201210143003709-597108252.png) 上面的思維導圖能夠概括了 Spring AOP,其最重要的是 Spring AOP `只能作用於 Bean`,而 AspectJ 能夠在編譯期、類載入期對位元組碼進行更改。 # 猜測實現原理 Spring AOP 的實現原理是`動態代理`,但是具體又是怎麼實現的呢? 在 Spring 容器中,我們使用的每個 bean 都是 BeanDefinition 的例項,容器會在合適的時機根據 BeanDefinition 的基本資訊例項化 bean 物件。 所以比較簡單的做法是,Spring 會自動生成代理物件的代理類。我們在獲取 bean 時,Spring 容器返回代理類物件,而不是實際的 bean。 # 除錯程式碼 本文使用的程式碼,安裝了 `lombok`,並基於 `Spring Boot`,是一個完全基於註解的最簡除錯程式碼。 註解配置類 AopConfig: ```java @Slf4j @Component @Aspect public class AopConfig { @Pointcut("within(com.life.demo..*)") public void pointCut() { } @Before("com.life.demo.AopConfig.pointCut()") public void log() { log.info("this is point cut..."); } } ``` Spring 啟動類 AppApplication: ```java @SpringBootApplication @EnableAspectJAutoProxy public class AppApplication { public static void main(String[] args) { SpringApplication.run(AppApplication.class, args); } } ``` Controller HelloWorldController: ```java package com.life.demo.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import lombok.extern.slf4j.Slf4j; @RestController @Slf4j public class HelloWorldController { @GetMapping("/hello") public String greeting() { return "hello!"; } } ``` 執行 Web 應用,在瀏覽器輸入網址 http://localhost:11111/hello,會看到 log: ``` INFO 96257 --- [io-11111-exec-1] com.life.demo.AopConfig : this is point cut... ``` 驗證出成功配置了代理。 # 使用說明 1. @EnableAspectJAutoProxy 開啟 AOP。 2. 使用 @Aspect 註解的 bean 都會被 Spring 當做用來實現 AOP 的配置類。 3. 配置 Advice,不做詳細介紹,具體參考 [Spring AOP 官方文件](https://docs.spring.io/spring/docs/2.0.x/reference/aop.html)。 3. @Pointcut,用來匹配 Spring 容器中的所有 bean 的方法的。 ```java @Pointcut("execution(* transfer(..))")// the pointcut expression private void anyOldTransfer() {}// the pointcut signature ``` @Pointcut 中使用了 execution 來正則匹配方法簽名,這也是最常用的,除了 execution,我們再看看其他的幾個比較常用的匹配方式: - within:指定所在類或所在包下面的方法(Spring AOP 獨有) `如 @Pointcut("within(com.javadoop.springaoplearning.service..*)")` - @annotation:方法上具有特定的註解,如 @Subscribe 用於訂閱特定的事件。 `如 @Pointcut("execution(* .(..)) && @annotation(com.javadoop.annotation.Subscribe)")` - bean(idOrNameOfBean):匹配 bean 的名字(Spring AOP 獨有) `如 @Pointcut("bean(*Service)")` Tips:上面匹配中,通常 "." 代表一個包名,".." 代表包及其子包,方法引數任意匹配使用兩個點 ".."。 # 原始碼深入分析 ## @EnableAspectJAutoProxy 開啟 AOP @EnableAspectJAutoProxy 註解定義: ```java @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(AspectJAutoProxyRegistrar.class) public @interface EnableAspectJAutoProxy { boolean proxyTargetClass() default false; boolean exposeProxy() default false; } ``` ```java class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions( AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry); AnnotationAttributes enableAspectJAutoProxy = AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class); if (enableAspectJAutoProxy != null) { if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) { AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); } if (enableAspectJAutoProxy.getBoolean("exposeProxy")) { AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry); } } } } ``` 在 AppApplication 啟動類上要加入 `@EnableAspectJAutoProxy` 註解開啟 AOP,檢視該註解原始碼,其 proxyTargetClass() 是在 AspectJAutoProxyRegistrar 類中呼叫,而 AspectJAutoProxyRegistrar 是一個 ImportBeanDefinitionRegistrar。再往上追根溯源,可以看到是在介面 ConfigurableApplicationContext 中 void refresh() 呼叫。 ## IOC 容器管理 AOP 例項 在建立 bean 時,會呼叫 AbstractAutowireCapableBeanFactory#doCreateBean(...)。 ```java protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) throws BeanCreationException { // 初始化 bean BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { // 1. 建立例項 instanceWrapper = createBeanInstance(beanName, mbd, args); } ... // Initialize the bean instance. Object exposedObject = bean; try { // 2. 裝載屬性 populateBean(beanName, mbd, instanceWrapper); if (exposedObject != null) { // 3. 初始化 exposedObject = initializeBean(beanName, exposedObject, mbd); } } ... } ``` 著重看第3步 initializeBean(...) 方法: ```java protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) { if (System.getSecurityManager() != null) { AccessController.doPrivileged((PrivilegedAction