Spring原始碼分析之AOP從解析到呼叫
阿新 • • 發佈:2020-12-14
正文:
在上一篇,我們對IOC核心部分流程已經分析完畢,相信小夥伴們有所收穫,從這一篇開始,我們將會踏上新的旅程,即Spring的另一核心:AOP!
首先,為了讓大家能更有效的理解AOP,先帶大家過一下AOP中的術語:
- **切面(Aspect)**:指關注點模組化,這個關注點可能會橫切多個物件。事務管理是企業級Java應用中有關橫切關注點的例子。在Spring AOP中,切面可以使用在普通類中以@Aspect註解來實現。
- **連線點(Join point)**:在Spring AOP中,一個連線點總是代表一個方法的執行,其實就代表增強的方法。
- **通知(Advice)**:在切面的某個特定的連線點上執行的動作。通知有多種型別,包括`around`, `before`和`after`等等。許多AOP框架,包括Spring在內,都是以攔截器做通知模型的,並維護著一個以連線點為中心的攔截器鏈。
- **目標物件(Target**):目標物件指將要被增強的物件。即包含主業務邏輯的類的物件。
- **切點(Pointcut)**:匹配連線點的斷言。通知和切點表示式相關聯,並在滿足這個切點的連線點上執行(例如,當執行某個特定名稱的方法時)。切點表示式如何和連線點匹配是AOP的核心:Spring預設使用AspectJ切點語義。
- **顧問(Advisor)**: 顧問是Advice的一種包裝體現,Advisor是Pointcut以及Advice的一個結合,用來管理Advice和Pointcut。
- **織入(Weaving)**:將通知切入連線點的過程叫織入
- **引入(Introductions)**:可以將其他介面和實現動態引入到targetClass中
## 一個栗子
術語看完了,我們先上個Demo回顧一下吧~
1. 首先,使用`EnableAspectJAutoProxy`註解開啟我們的AOP
```java
@ComponentScan(basePackages = {"com.my.spring.test.aop"})
@Configuration
@EnableAspectJAutoProxy
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main.class);
IService service = context.getBean("service", IService.class);
service.doService();
}
}
```
2. 寫一個介面
```java
public interface IService {
void doService();
}
```
3. 寫一個實現類
```java
@Service("service")
public class ServiceImpl implements IService{
@Override
public void doService() {
System.out.println("do service ...");
}
}
```
4. 寫一個切面
```java
@Aspect
@Component
public class ServiceAspect {
@Pointcut(value = "execution(* com.my.spring.test.aop.*.*(..))")
public void pointCut() {
}
@Before(value = "pointCut()")
public void methodBefore(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.println("執行目標方法 【" + methodName + "】 的【前置通知】,入參:" + Arrays.toString(joinPoint.getArgs()));
}
@After(value = "pointCut()")
public void methodAfter(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.println("執行目標方法 【" + methodName + "】 的【後置通知】,入參:" + Arrays.toString(joinPoint.getArgs()));
}
@AfterReturning(value = "pointCut()")
public void methodReturn(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.println("執行目標方法 【" + methodName + "】 的【返回通知】,入參:" + Arrays.toString(joinPoint.getArgs()));
}
@AfterThrowing(value = "pointCut()")
public void methodThrow(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.println("執行目標方法 【" + methodName + "】 的【異常通知】,入參:" + Arrays.toString(joinPoint.getArgs()));
}
}
```
5. 測試執行
```
執行目標方法 【doService】 的【前置通知】,入參:[]
do service ...
執行目標方法 【doService】 的【返回通知】,入參:[]
執行目標方法 【doService】 的【後置通知】,入參:[]
```
> 以上
Demo看完了,執行效果也出來了,AOP已生效,但如何生效的呢?相比於我們普通使用Bean的Demo,在這裡,我們只不過加上了一個`@EnableAspectJAutoProxy`註解以及一個標識了`@Aspectj`的類,那麼我們先看看`@EnableAspectJAutoProxy`這個註解做了什麼吧~
## 開啟AOP
以下是筆者所畫的大致流程圖
![](https://img2020.cnblogs.com/other/1187061/202012/1187061-20201214083728608-1653671399.png)
其中`AspectJAutoProxyRegistrar`實現了`ImportBeanDefinitionRegistrar`,所以在處理`BeanFactoryPostProcessor`邏輯時將會呼叫`registerBeanDefinitions`方法,此時就會把`AnnotationAwareAspectJAutoProxyCreator`註冊到容器中,其中`BeanFactoryPostProcessor`的邏輯就不再說了,往期文章有過詳細分析。而`AnnotationAwareAspectJAutoProxyCreator`的類圖如下:
![](https://img2020.cnblogs.com/other/1187061/202012/1187061-20201214083729672-1502649614.png)
我們發現`AnnotationAwareAspectJAutoProxyCreator`是實現了`BeanPostProcessor`介面的類,所以它其實是一個後置處理器,那麼,還記得在建立Bean過程中的`BeanPostProcessor`九次呼叫時機嗎?不記得也沒關係,`AnnotationAwareAspectJAutoProxyCreator`起作用的地方是在bean的例項化前以及初始化後,分別對應著解析切面和建立動態代理的過程,現在,就讓我們先來看看解析切面的過程吧~
## 解析切面
解析切面的流程如下圖所示:
![](https://img2020.cnblogs.com/other/1187061/202012/1187061-20201214083734567-465483733.png)
我們已經瞭解到切面解析的過程是由`AnnotationAwareAspectJAutoProxyCreator`完成的,而`AnnotationAwareAspectJAutoProxyCreator`又繼承了`AbstractAutoProxyCreator`,所以首先,我們先會來到`AbstractAutoProxyCreator#postProcessBeforeInstantiation`
```java
public Object postProcessBeforeInstantiation(Class beanClass, String beanName) {
// class型別是否為(Advice, Pointcut, Advisor, AopInfrastructureBean)
// shouldSkip中將會解析切面
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null;
}
}
```
呼叫到子類的`AspectJAwareAdvisorAutoProxyCreator#shouldSkip`
```java
@Override
protected boolean shouldSkip(Class beanClass, String beanName) {
// 尋找adviso