如何理解 Spring AOP 以及使用 AspectJ?

2021-01-03 CSDN

作者 | 阿文

責編 | 屠敏

在 Spring 中 AOP 是一個非常非常重要的概念,那麼什麼是AOP呢?

AOP 即面向切面編程,也可以叫做面向方向編程,AOP不是一個新東西,它是OOP,即面向對象編程的一種補充,在當前已經成為一種成熟的編程方式。

為啥要使用 AOP

在學習AOP 之前,我們先了解下為啥我們要使用AOP?

那麼,在傳統的業務處理代碼中,比如你要操作資料庫,會進行事務的處理或者列印一些日誌。雖然通過OOP 也可以實現,比如通過繼承或組合的方式來達到代碼的復用,但是如果實現某些功能,比如日誌記錄,相同的代碼會分散到各個方法中,如果後面要想關閉某個功能或進行修改就必須要修改所有的方法,非常的不方便。

那麼為了解決為了解決這個問題,AOP的思想隨之產生。它採取了橫向抽取機制。將分散在各個方法中的重複代碼提取出來,然後在程序編譯或運行時,再將這些提取出來的代碼應用到需要執行的地方。這種採用橫向提取機制的方式。是採用傳統的AOP方式下無法辦到的。因為傳統的面向對象思想只能實現父子關係的縱向重用。

在AOP思想中,通過aspect(切面)可以分別在不同的類的方法中加入,例如事務日誌權限和異常處理等功能。

使用切面這種橫向方式。能夠使開發人員在編寫業務邏輯時專注於核心業務邏輯,而不用過度的關注與其他業務邏輯的實現。這樣可以提高開發效率,同時還增強了代碼的可維護性。

目前主流的AOP 框架有2個,分別是spring aop 和aspectJ,前者是純Java 實現的,不需要專門的編譯過程和類加載器,在運行期間可以通過代理的方式向目標內植入增強的代碼。而AspectJ是一個基於Java語言的AOP框架。在Spring 2.0 開始,引入了對AspectJ 的支持,並提供了一個專門的編譯器在編譯時提供橫向代碼的植入。

相關術語

在了解AOP之前,首先要了解一下它的專業術語。這些術語包括Aspect、Joinpiont、Pointcut、Advice、Target Object、Proxy 和Weaving,對於這些專業術語具體的解釋如下:

Aspect,切面在實際的應用中,切面通常是指封裝的用於橫向插入系統功能,比如事務日誌的類,該類被spring容器識別為切面,需要在配置文件中通過\<bean\>來指定。Joinpiont,連接點,在程序執行過程中的某個階段點。它實際上是對象的一個操作,例如方法的調用或異常的拋出。在spring AOP中連接點就是指方法的調用。Pointcut,切入點,切入點是指切面與程序流程的交叉點,即那些需要處理的連接點,通常在程序中切入點是指。類或者是方法名,比如說某個通知要應用到所有的以add開頭的方法中。那麼所有滿足這一規則的方法都是切入點。Adivce,通知增強處理,AOP 框架在特定的切入點執行增強處理,即在定義好的切入點處理所需要執行的程序代碼,你可以理解其為切面類中的方法,它是切面的具體實現。Target Object ,目標對象,是指所有通知的對象。也稱為北增強對象。如果AOP框架採用的是動態的AOP實現,那麼該對象就是一個被代理對象。Proxy ,代理,將通知應用到目標對象之後被動態創建的對象。Weaving, 織入,將切面代碼插入目標對象上,從而生成代理對象的過程。

AspectJ 開發

使用AspectJ 實現AOP 的方式有

XML 聲明註解XML 聲明

這種方式是通過XML文件來定義切面、切入點以及通知等,所有的切面、切入點和通知都必須定義在<aop:config>元素中,在<beans>元素中可以包含多個<aop:config>元素,一個 <aop:config> 中又可以包含子元素和屬性,其子元素包含<aop:pointcut、<aop:advisor、<aop:aspect>,在配置時,需要嚴格按照順序來定義,在<aop:aspect>下,同樣包含屬性和多個子元素,通過使用<aop:aspect>元素以及其子元素 可以在XML中配置切面、切入點和通知。如下所示

<?xml version="1.0" encoding="UTF-8"?><beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd"><!-- 1 目標類 --><beanid="userDao"class="com.ssm.aspectj.UserDaoImpl" /><!-- 2 切面 --><beanid="myAspect"class="com.ssm.aspectj.xml.MyAspect" /><!-- 3 aop編程 --><aop:config><!-- 1.配置切面 --><aop:aspectid="aspect"ref="myAspect"><!-- 2.配置切入點 --><aop:pointcutexpression="execution(* com.ssm.aspectj.*.*(..))"id="myPointCut" /><!-- 3.配置通知 --><!-- 前置通知 --><aop:beforemethod="myBefore"pointcut-ref="myPointCut" /><!--後置通知--><aop:after-returningmethod="myAfterReturning"pointcut-ref="myPointCut"returning="returnVal"/><!--環繞通知 --><aop:aroundmethod="myAround"pointcut-ref="myPointCut" /><!--異常通知 --><aop:after-throwingmethod="myAfterThrowing"pointcut-ref="myPointCut"throwing="e" /><!--最終通知 --><aop:aftermethod="myAfter"pointcut-ref="myPointCut" /></aop:aspect></aop:config></beans>

但是 XML 的配置過於複雜,因為日常開發過程中,我們更傾向於使用註解的方式來進行AOP的開發

為了在應用中使用@AspectJ支持,Spring需要添加三個庫:

aspectjweaver.jaraspectjrt.jaraopalliance.jar因此,我們需要配置maven,如下所示

<dependency><groupId>org.aspectj</groupId><artifactId>aspectjrt</artifactId><version>1.6.12</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.6.12</version></dependency>

新建一個app.xml ,我們需要在Spring配置文件中做如下配置:

<?xml version="1.0" encoding="UTF-8"?><beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"><!-- 指定需要掃描的包,使註解生效 --><context:component-scanbase-package="com.ssm.aspectj" /><!-- 啟動基於註解的聲明式AspectJ支持 --><aop:aspectj-autoproxy /></beans>

然後,我們創建一個UserDao 的接口,如下所示

package com.ssm.aspectj;publicinterfaceUserDao{// add userpublicvoidaddUser();//delete userpublicvoiddelUser();}

將 UserDao 實例化,並加上註解@Repository("userDao"),方便後續進行調用具體的方法

package com.ssm.aspectj;import org.springframework.stereotype.Repository;@Repository("userDao")publicclassUserDaoImplimplementsUserDao{@OverridepublicvoidaddUser(){ System.out.println("add user"); }@OverridepublicvoiddelUser(){ System.out.println("delete user"); }}

定義一個切面類,在該類中編寫各種通知

package com.ssm.aspectj.xml;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.After;import org.aspectj.lang.annotation.AfterReturning;import org.aspectj.lang.annotation.AfterThrowing;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.aspectj.lang.annotation.Pointcut;import org.springframework.stereotype.Component;/** * 切面類,在此類中編寫通知 */@Aspect@ComponentpublicclassMyAspect{//定義切入點表達式@Pointcut("execution(* com.ssm.aspectj.*.*(..))")//使用一個返回值為void、方法體為空的方法來命名切入點private void myPointCut(){}//前置通知@Before("myPointCut()")public void myBefore(JoinPoint joinPoint){ System.out.print("前置通知:模擬執行權限檢查..,"); System.out.print("目標類是:"+joinPoint.getTarget()); System.out.println(",被植入增強處理的目標方法為:"+joinPoint.getSignature().getName()); }//後置通知@AfterReturning(value="myPointCut()")public void myAfterReturning(JoinPoint joinPoint) { System.out.print("後置通知:模擬記錄日誌..,"); System.out.println("被植入增強處理的目標方法為:" + joinPoint.getSignature().getName()); }/** * 環繞通知 * ProceedingJoinPoint是JoinPoint的子接口,表示可執行目標方法 * 1.必須是Object類型的返回值 * 2.必須接收一個參數,類型為ProceedingJoinPoint * 3.必須throws Throwable */@Around("myPointCut()")public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{//開始 System.out.println("環繞開始:執行目標方法之前,模擬開啟事務..,");//執行當前目標方法 Object obj=proceedingJoinPoint.proceed();//結束 System.out.println("環繞結束:執行目標方法之後,模擬關閉事務..,");return obj; }//異常通知@AfterThrowing(value="myPointCut()",throwing="e")public void myAfterThrowing(JoinPoint joinPoint,Throwable e){ System.out.println("異常通知:出錯了"+e.getMessage()); }//最終通知@After("myPointCut()")public void myAfter(){ System.out.println("最終通知:模擬方法結束後釋放資源.."); }}

srping 的通知包括五種通知工作:

在上述代碼中,其中 @Pointcut("execution(* com.ssm.aspectj.*.*(..))") 表示定義切入點,使用註解@Pointcut 後面的execution(* com.ssm.aspectj.*.*(..)) 表示匹配所有目標類的所有方法。第一個*代表返回類型,第二個*代表方法名,而..代表任意入參的方法,他的格式如下:

語法:execution(修飾符 返回值 包.類.方法(參數) throws 異常)

最後編寫個測試類從ClassPathXmlApplicationContext 讀取xml 文件,然後調用getBean 獲取userDao並執行addUser方法。

package com.ssm.aspectj;import org.junit.jupiter.api.Test;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;publicclassTestXmlAspectJ{@TestpublicvoidtestAnnotation(){ ApplicationContext applicationContext=new ClassPathXmlApplicationContext("app.xml");//從容器中獲得內容 UserDao userDao= (UserDao) applicationContext.getBean("userDao");//執行方法 userDao.addUser(); }}

結果如下:

相關焦點

  • Spring學習(5):基於AspectJ的AOP開發(上)
    新版本的Spring框架建議使用AspectJ方式來進行開發AOP。使用AspectJ需要導入Spring AOP和AspectJ相關的jar包,如spring-aop、aopalliance、aspects以及aspectj.weaver等。
  • 一起來談談 Spring AOP
    切點(Pointcut) 哈哈,這個你可能就比較容易理解了,切點在Spring AOP中確實是對應系統中的方法。但是這個方法是定義在切面中的方法,一般和通知一起使用,一起組成了切面。這可以在編譯時完成(例如使用AspectJ編譯器),也可以在運行時完成。Spring和其他純Java AOP框架一樣,在運行時完成織入。
  • Spring框架AOP技術
    " + e.getMessage()); }}3.6編寫配置文件spring-aop-aspectj.xml<?xml version="1.0" encoding="UTF-8"?
  • Spring中的IOC和AOP以及Spring中的聲明式事務
    -- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver --><dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version
  • Spring框架IOC和AOP簡介
    ><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation
  • Java語言編程第41講——深入淺出理解Spring AOP
    下面用一個需求的各種編碼實現方案,來講解Spring AOP,讓你在閱讀簡單源碼的同時,輕鬆地理解Spring AOP的作用和優勢。這樣一個需求:給定一組算法,計算各種算法的耗時。我們該如何實現呢?如何保持自己的代碼是POJO形式,不受超類或者接口入侵?下面介紹最完美的方案:使用AOP。
  • Spring:AOP 面向切面編程
    在正式講解 AOP 的操作之前,我們必須理解 AOP 的相關術語,常用的術語如下:* Target(目標對象):代理的目標對象;被代理類* Proxy(代理):一個類被 AOP 織入增強後,就產生一個結果代理類;生成代理對象* Joinpoint(連接點):所謂連接點是指那些可以被攔截到的點;在 spring 中,這些點指的是方法,因為 spring 只支持方法類型的連接點
  • spring框架的入門學習:AOP和面向切面的事務
    gt;這個配置的就是說spring會去com.huanfeng.bean中掃描所有的註解(包括bean包下面的所有類以及子包下的所有類),那麼我們下面就可以在com.huanfeng.bean下的所有類中使用註解配置文件了3.在類中使用註解配置文件@Component(&34;)
  • 使用AspectJ註解技術實現AOP功能
    AspectJ是一個面向切面編程的框架,使用AspectJ不需要改動Spring配置文件,就可以實現Spring AOP功能。本篇結合實際案例詳細講述使用AspectJ實現AOP功能。通過本篇的學習,可以解決如下問題。● 使用AspectJ技術的背景是什麼?● 在不修改原有業務代碼的情況下,如何設置業務攔截點?
  • 你知道面試必問的AOP嗎?通過Spring又如何實現呢?
    配置添加命名空間xmlns:aop=&34;http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
  • Java 第一大框架:Spring 的 IoC 跟 AOP 雛形如何實現?
    :aop="http://www.springframework.org/schema/aop"xmlns:p="http://www.springframework.org/schema/p"xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/
  • Spring 5 中文解析核心篇-IoC容器之AOP編程
    要使用本節中描述的aop名稱空間標籤,內需要導入spring-aop模式,如基於XML Schema的配置中所述。有關如何在aop名稱空間中導入標籤的信息,請參見AOP schema。在你的Spring配置中,所有切面和advisor元素都必須放在<aop:config>元素內(在應用程式上下文配置中可以有多個<aop:config>元素)。
  • Spring學習(6):基於AspectJ的AOP開發(下)
    import org.aspectj.lang.JoinPoint;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;/** * 切面類 **/@Aspectpublic class AspectJByAnno { //前置通知類型 @Before
  • Spring AOP 用註解封裝 redis 緩存
    ><beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi
  • Spring AOP加載流程
    查看spring-aop模塊可以看到如下配置http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler該處理類如下
  • Spring4基礎二七-AOP篇-AspectJ的XML配置
    spring-aspects-4.2.1.RELEASE.jarcom.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar三.XML配置演示1.定義接口-- 配置切面 --><aop:config> <!
  • Spring AOP源碼分析
    :aspectj-autoproxy/>spring-aop.xml<!-- 自動為spring容器中那些配置@AspectJ切面的bean創建代理,織入切面 proxy-target-class默認為false,表示使用jdk動態代理織入增強, 當配為true時,表示使用Cglib動態代理技術織入增強--><aop:aspectj-autoproxy proxy-target-class="true"/><aop:config&
  • 詳解Spring框架的AOP機制
    ● 理解AOP的編程思想及原理● 掌握AOP的實現技術Spring框架的AOP機制可以讓開發者把業務流程中的通用功能抽取出來,單獨編寫功能代碼。在業務流程執行過程中,Spring框架會根據業務流程要求,自動把獨立編寫的功能代碼切入到流程的合適位置。
  • SpringBoot:切面AOP權限校驗:實例演示與註解全解
    完成項目已上傳至:https://github.com/ThinkMugz/aopDemo使用 AOP,首先需要引入 AOP 的依賴。參數校驗:這麼寫參數校驗(validator)就不會被勸退了~<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId></dependency
  • java 註解結合 spring aop 實現自動輸出日誌
    所以希望從從簡到繁實現一個工具,便於平時使用。;import org.aspectj.lang.Signature;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Pointcut;import org.aspectj.lang.reflect.MethodSignature