Spring——AOP原理及源碼一【系列完】


系列介紹

共分為五篇,按照AOP的運行流程演示並分析springAOP源碼,總結流程

系列流程

從AOP實例的構建到重要組件分析、基本運行流程、關鍵方法調用、原理總結等幾個方面一步步分解AOP源碼

本篇概述

為讀者演示構建AOP實例及AOP核心組件分析


 

 

一、項目構建

讀者可直接下載示例工程,或復制以下的代碼到本地工程開啟教程。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.tlj</groupId>
    <artifactId>spring-test</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>4.3.13.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.3.13.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
            <scope>test</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/javax.inject/javax.inject -->
        <dependency>
            <groupId>javax.inject</groupId>
            <artifactId>javax.inject</artifactId>
            <version>1</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.2</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.44</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>4.3.12.RELEASE</version>
        </dependency>
    </dependencies>


</project>
pom.xml
package config;

import aop.LogAspects;
import aop.MathCalculator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@EnableAspectJAutoProxy
@Configuration
public class ConfigOfAOP {

    @Bean
    public MathCalculator calculator(){
        return new MathCalculator();
    }

    @Bean
    public LogAspects logAspects(){
        return new LogAspects();
    }
}
ConfigOfAOP
package aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;

import java.util.Arrays;

/**
 * 切面類
 */
@Aspect
public class LogAspects {

    @Pointcut("execution(public int aop.MathCalculator.*(..))")
    public void poinCut(){}

    @Before("poinCut()")
    public void logStart(JoinPoint joinPoint){
        Object[] args = joinPoint.getArgs();
        System.out.println(joinPoint.getSignature().getName()+" 運行。。。@Before "+ Arrays.asList(args));
    }

    @After("poinCut()")
    public void  logEnd(){
        System.out.println("除法結束..@After");
    }

    @AfterReturning(value = "poinCut()",returning = "result")//獲取方法返回值
    public void logReturning(Object result){
        System.out.println("除法正常返回..@AfterReturning "+result);
    }

    @AfterThrowing(value = "poinCut()",throwing = "e")
    public void logException(Exception e){
        System.out.println("除法異常..@AfterThrowing "+e);
    }
}
LogAspects
package aop;

public class MathCalculator {

    public int div(int i,int j){
        System.out.println("MathCalculator");
        return i/j;
    }
}
MathCalculator

項目目錄結構如下:

到這里,構建了一個簡單的切面功能demo


 

 

 

二、運行測試

打開測試類,運行測試方法


 

最終效果

總共打印了四行,第二行是業務方法的調用,其他都是調用日志切面類中的方法打印的。

這就是AOP的使用效果,除了用在日志,還有其他很多用法,這里就不贅述了。

 

 

三、關鍵組件探究

為什么AOP能在業務方法調用的前后和發生異常時調用切面方法呢,首先我們需要了解它引入了什么組件。

為了讓AOP起作用,我們需要在配置類上添加@EnableAspectJAutoProxy注解,從字面上看,翻譯為啟動切面自動代理,那它是怎么啟動的呢

ctrl+鼠標左鍵進入這個注解,我們可以看到EnableAspectJAutoProxy接口使用@Import注解導入了AspectJAutoProxyRegistrar這個類


 

再次ctrl+鼠標左鍵進入AspectJAutoProxyRegistrar,可以看到,它實現了ImportBeanDefinitionRegistrar接口。

此接口中的registerBeanDefinitions方法,正是用來向容器中注冊組件的。

接下來來看@EnableAspectJAutoProxy注解到底給容器中注冊了什么組件。這是AOP實現的關鍵。

 

 

四、調試尋找組件

如下圖,我們在ImportBeanDefinitionRegistrar接口的注冊方法中打上斷點。


 

點擊debug開始調試,程序來到了AspectJAutoProxyRegistrar的registerBeanDefinitions方法

正在執行的是AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry)

(字面意思為:注冊切面自動代理創造組件如果需要的話)


 

接着進入這個方法直到以下這個方法,可以看到返回的是BeanDefinition類型,而BeanDefinition是用來保存bean定義信息的

上圖所示,一進來先進行判斷,如果容器中存在AUTO_PROXY_CREATOR_BEAN_NAME定義信息,進行一些操作,最后return null。

如果不存在,可以看到在125行已經有注冊名為AUTO_PROXY_CREATOR_BEAN_NAME的組件的動作,要將定義信息注冊到容器中。

把鼠標放在AUTO_PROXY_CREATOR_BEAN_NAME上,可以看到它名為internalAutoProxyCreator

 

接着我們進行下一步,到110行時,顯然第一次容器中不存在這個類,所以跳過if{}中的內容

到121行時,通過bean的各種定義信息,new一個定義bean,用來保存這個bean的各種定義信息

通過cls的信息,發現注冊的internalAutoProxyCreator實際為AnnotationAwareAspectJAutoProxyCreator

 

到125行時,已經設置好AnnotationAwareAspectJAutoProxyCreator的各種屬性

並將其命名為internalAutoProxyCreator注冊進容器,在126行進行返回。

注意:這里注冊的是BeanDefinition,也就是bean的定義信息,並沒有創建bean實例

總結

到此為止@EnableAspectJAutoProxy給容器中添加了internalAutoProxyCreator的BeanDefinition組件

internalAutoProxyCreator中實際又是AnnotationAwareAspectJAutoProxyCreator這個類

所以我們可以得出AnnotationAwareAspectJAutoProxyCreator就是實現AOP的核心組件

接下來我們來看AnnotationAwareAspectJAutoProxyCreator的繼承關系,看它到底是什么

 

 

五、AnnotationAwareAspectJAutoProxyCreator組件是什么

進入這個類,發現它繼承了AspectJAwareAdvisorAutoProxyCreator

 

那么AspectJAwareAdvisorAutoProxyCreator又是什么呢,接着進入AspectJAwareAdvisorAutoProxyCreator

發現AspectJAwareAdvisorAutoProxyCreator又繼承了AbstractAdvisorAutoProxyCreator

我們接着進入AbstractAdvisorAutoProxyCreator中查看

可以看到AbstractAdvisorAutoProxyCreator繼承了AbstractAutoProxyCreator

再進入AbstractAutoProxyCreator

可以看到AbstractAutoProxyCreator繼承了ProxyProcessorSupport

並實現了SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware兩個接口

由於接下來繼承的ProxyProcessorSupport是一個基礎工具類,就不往下分析了


 

接下來我們主要看這兩個接口

SmartInstantiationAwareBeanPostProcessor:從名字中帶有PostProcessor看出是一個后置處理器接口

BeanFactoryAware:底層組件接口,和其他XXXAware一樣,實現XXXAware接口就可以調用XXX組件

經過層層的進入,可以得到如下的關系:

 

從圖得出結論——AnnotationAwareAspectJAutoProxyCreator是一個后置處理器后置處理器原理

 

 

總結

  經過以上五個步驟,我們了解了AOP的使用效果

  找到了AOP的核心組件AnnotationAwareAspectJAutoProxyCreator

  理清了AnnotationAwareAspectJAutoProxyCreator的繼承實現關系

  發現了AOP的核心組件本質上是一個后置處理器

 

下篇預知:在下一篇中我們將從核心組件在哪發揮作用,何時發揮,以及做了什么,一步步深入原理。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM