spring——AOP原理及源碼(一)

来源:https://www.cnblogs.com/Unicron/archive/2020/03/01/12386776.html
-Advertisement-
Play Games

教程共分為五篇,從AOP實例的構建及其重要組件、基本運行流程、容器創建流程、關鍵方法調用、原理總結歸納等幾個方面一步步走進AOP的世界。 本篇主要為讀者演示構建AOP實例及AOP核心組件分析。 一、項目構建 讀者可直接下載示例工程,或複製以下的代碼到本地工程開啟教程。 <?xml version=" ...


教程共分為五篇,從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

項目目錄結構如下:

到這裡,我們的項目是構建完了。

 

 

二、日誌切麵方法測試

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

 我們可以看到,總共列印了四行,除了第二行列印是業務方法的調用,其他都是調用日誌切麵類中的方法列印出來的。

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

 

 

三、關鍵組件探究

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

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

ctrl+滑鼠左鍵進入這個註解,我們可以看到EnableAspectJAutoProxy介面使用@Import註解導入了AspectJAutoProxyRegistrar這個類

再次ctrl+滑鼠左鍵進入AspectJAutoProxyRegistrar,可以看到,它實現了ImportBeanDefinitionRegistrar介面。

此介面中的registerBeanDefinitions方法,正是用來像容器中註冊組件的。

看來想要知道@EnableAspectJAutoProxy註解到底給容器中添加了什麼組件,我們需要進行調試,找到ImportBeanDefinitionRegistrar方法給容器中添加的組件。這個組件一定就是AOP實現的關鍵。

 

 

 四、調試尋找組件

如下圖,我們在ImportBeanDefinitionRegistrar介面的註冊方法中打上斷點。

 

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

正在執行的是AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry),方法的字面意思:註冊切麵自動代理創造組件如果需要的話

 

接著進入這個方法直到以下這個方法,可以看到返回的是BeanDefinition類型,說明在方法裡面有組件定義或註冊相關的動作。

 110行進行判斷,如果容器中存在AUTO_PROXY_CREATOR_BEAN_NAME這個定義信息,進行以下判斷,巴拉巴拉,最後return null,退出這個方法。

如果不存在,可以看到在125行已經有註冊名為AUTO_PROXY_CREATOR_BEAN_NAME的組件的動作。

把滑鼠放在AUTO_PROXY_CREATOR_BEAN_NAME上,可以看到它實際是叫internalAutoProxyCreator

接著我們進行下一步,到110行時,顯然第一次它是不存在這個類的,所以跳過if{}中的內容,到121行時,我們可以看看cls的信息,發現這個類叫AnnotationAwareAspectJAutoProxyCreator

 

 到125行時,已經設置好AnnotationAwareAspectJAutoProxyCreator的各種屬性,將其命名為internalAutoProxyCreator註冊進容器,在126行進行返回。

在上面過程中,我們可以得到的結論是,@EnableAspectJAutoProxy註解實際上就是給容器中添加了名為internalAutoProxyCreator的組件,實際就是AnnotationAwareAspectJAutoProxyCreator這個類。

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

接下來我們來探究一下AnnotationAwareAspectJAutoProxyCreator的繼承關係,以及它是什麼。

 

 

五、AnnotationAwareAspectJAutoProxyCreator組件是什麼

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

 

那麼AspectJAwareAdvisorAutoProxyCreator又是什麼呢,接著進入AspectJAwareAdvisorAutoProxyCreator

發現AspectJAwareAdvisorAutoProxyCreator又繼承了AbstractAdvisorAutoProxyCreator

我們接著進入AbstractAdvisorAutoProxyCreator中查看

可以看到AbstractAdvisorAutoProxyCreator繼承了AbstractAutoProxyCreator

再進入AbstractAutoProxyCreator

可以看到AbstractAutoProxyCreator繼承了ProxyProcessorSupport

並實現了SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware兩個介面

 

 接下來我們主要看這兩個介面

SmartInstantiationAwareBeanPostProcessor明顯是一個後置處理器介面,BeanFactoryAware是一個底層組件介面,實現BeanFactoryAware就可以註入並調用BeanFactory。

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

這樣看來,我們可以得出結論——AnnotationAwareAspectJAutoProxyCreator是一個後置處理器後置處理器原理

 

 

總結

  經過以上五個步驟,我們看到AOP的使用效果,發現了AOP的核心組件AnnotationAwareAspectJAutoProxyCreator是一個後置處理器,理清了AnnotationAwareAspectJAutoProxyCreator的繼承實現關係。

總得來說,就是明白了核心組件是什麼。

 

在接下來的篇章我們將從核心組件在哪發揮作用,何時發揮,以及做了什麼,一步步深入原理。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 目錄 1. 隱式類型轉換 2. 顯示類型轉換/強制類型轉換( static_cast、const_cast、reinterpret_cast、dynamic_cast) 3. 類型轉換函數、轉換構造函數 類型轉換可分為 隱式類型轉換(編譯器自動完成) 與 顯示類型轉換(強制類型轉換,需要自己操作)。 ...
  • 先用一個數組表示一個二叉樹搜索樹,也就是一個排好序的二叉樹,其中左子結點<根結點<右子結點 利用結構數組的形式來表示,id , left , right 代表結點id ,左子樹 ,右子樹 下麵這個二維數組 $data[]=['id'=>8,'left'=>2,'right'=>10,'data'=> ...
  • 上一篇我們已經根據路徑讀取到了我們需要的位元組碼文件,就以java.lang.Object這個類為例,可以看到類似下麵這種東西,那麼這些數字是什麼呢? 要瞭解這個,我們大概可以猜到這是十進位的,線上將十進位轉為十六進位看看https://tool.oschina.net/hexconvert/,註意上 ...
  • [TOC] 下麵介紹pandas常見的基本功能,和python的基本數據類型進行比較可以看到pandas在操作大型數據集中的優勢。 1.重建索引 (1)函數:reindex (2)作用:創建一個符合新索引的新對象。 (3)內容: Series調用reindex方法時,會將數組按照新的索引進行排列,如 ...
  • 基於JSP+Servlet開發失物招領系統開發環境: Windows操作系統開發工具:Myeclipse+Jdk+Tomcat7+MYSQL資料庫運行效果圖 源碼及原文鏈接:https://javadao.xyz/forum.php?mod=viewthread&tid=115 ...
  • 基於JSP+Servlet開發公交線上查詢(前臺+後臺):( 開發環境: Windows操作系統開發工具: Eclipse+Jdk+Tomcat+MYSQL資料庫運行效果圖 源碼及原文鏈接:https://javadao.xyz/forum.php?mod=viewthread&tid=120 ...
  • @[toc] 1. Spring註解的源碼分析 1.1 我如何開始分析源碼的? ==這一部分可以略過直接看第1.2節== 想必程式員都會經過這樣一個階段,當 已經能夠熟練運用。並且它的 也能夠用到 ,程式員就會找進階的入口,這時候就想到了去瞭解源碼。 作為Java程式員,首先想到的一定是瞭解Spri ...
  • React非常快速是因為它從不直接操作DOM。 虛擬DOM是在DOM的基礎上建立了一個抽象層,對數據和狀態所做的任何改動,都會被自動且高效的同步到虛擬DOM,最後再批量同步到DOM中。 在React中,render執行的結果得到的並不是真正的DOM節點,而僅僅是JavaScript對象,稱之為虛擬D ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...