Spring中的AOP操作你了解嗎
一、AOP操作術(shù)語(yǔ)
1. 連接點(diǎn)
類(lèi)里面哪些方法可以被增強(qiáng),這些可以被增強(qiáng)的方法就稱(chēng)為連接點(diǎn)。
2. 切入點(diǎn)
實(shí)際被真正增強(qiáng)的方法,稱(chēng)為切入點(diǎn)。
3. 通知(增強(qiáng))
(1)實(shí)際增強(qiáng)的邏輯部分稱(chēng)為通知(增強(qiáng))
(2)通知有如下多種類(lèi)型,如下:
- 前置通知
- 后置通知
- 環(huán)繞通知
- 異常通知
- 最終通知(finally)
4. 切面
切面是一個(gè)動(dòng)作,指的是將通知應(yīng)用到切入點(diǎn)的過(guò)程,就叫做切面。
二、AOP操作
Spring 框架一般都是基于 AspectJ 實(shí)現(xiàn) AOP 操作。AspectJ 不是 Spring 組成部分,而是獨(dú)立 AOP 框架,一般把 AspectJ 和 Spirng 框架一起使用,進(jìn)行 AOP 操作 。
基于A(yíng)spectJ實(shí)現(xiàn)AOP操作有如下兩種方式:
- 基于 xml 配置文件實(shí)現(xiàn)
- 基于注解方式實(shí)現(xiàn)(使用)
2.1 切入點(diǎn)表達(dá)式
切入點(diǎn)表達(dá)式作用:知道對(duì)哪個(gè)類(lèi)里面的哪個(gè)方法進(jìn)行增強(qiáng) ,語(yǔ)法結(jié)構(gòu)如下所示:
execution([權(quán)限修飾符] [返回類(lèi)型] [類(lèi)全路徑] [方法名稱(chēng)]([參數(shù)列表]) )
如下圖所示:

編寫(xiě)切點(diǎn)示例如下:
1. 如對(duì)com.wyf.spring5.Book類(lèi)中的add進(jìn)行增強(qiáng),則切入點(diǎn)表達(dá)式如下:
execution(* com.wyf.spring5.Book.add(..))
2. 如對(duì)com.wyf.spring5.Book類(lèi)中所有的方法增強(qiáng):
execution(* com.wyf.spring5.Book.*(..))
3. 如對(duì) com.wyf.spring5包中的所有類(lèi),類(lèi)中的所有方法都進(jìn)行加強(qiáng)
execution(* com.wyf.spring5.*.* (..))
2.2 AOP操作(AspectJ 注解方式)
1) 首先我們創(chuàng)建一個(gè)類(lèi),并添加一個(gè)方法:
/** * 被增強(qiáng)類(lèi) */public class User { public void add(){ System.out.println("add*****"); }}/**
* 被增強(qiáng)類(lèi)
*/
public class User {
public void add(){
System.out.println("add*****");
}
}2)接著我們創(chuàng)建增強(qiáng)類(lèi)(編寫(xiě)增強(qiáng)邏輯)
在增強(qiáng)類(lèi)中創(chuàng)建方法,讓不同的方法代表不同的通知類(lèi)型
/**
* 增強(qiáng)類(lèi)
*/
public class UserProxy {
/**
* 前置通知邏輯
*/
public void before(){
System.out.println("before*****");
}
}3)進(jìn)行通知的配置
在spring配置文件中,開(kāi)啟注解掃描。(采用java配置類(lèi)或xml配置文件實(shí)現(xiàn))
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 開(kāi)啟注解掃描 --> <context:component-scan base-package="com.wyf.aopanno"></context:component-scan> <!-- 開(kāi)啟Aspect 生成代理對(duì)象 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans><?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 開(kāi)啟注解掃描 -->
<context:component-scan base-package="com.wyf.aopanno"></context:component-scan>
<!-- 開(kāi)啟Aspect 生成代理對(duì)象 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>- 使用注解創(chuàng)建User和UserProxy對(duì)象(類(lèi)上面添加創(chuàng)建對(duì)象注解,此處為 @Component)
- 在增強(qiáng)類(lèi)上面添加注解@Aspect
/**
* 被增強(qiáng)類(lèi)
*/
@Component
public class User {
public void add(){
System.out.println("add*****");
}
}
/**
* 增強(qiáng)類(lèi)
*/
@Component
@Aspect //生成代理對(duì)象
public class UserProxy {
/**
* 前置通知邏輯
*/
public void before(){
System.out.println("before*****");
}
}在 spring 配置文件中開(kāi)啟生成代理對(duì)象
<!-- 開(kāi)啟Aspect 生成代理對(duì)象 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>開(kāi)啟Aspect生成代理對(duì)象,相當(dāng)掃描帶有@Aspect注解的類(lèi),并生成該對(duì)象的一個(gè)代理對(duì)象。
4)配置不同類(lèi)型通知
在增強(qiáng)類(lèi)的里面,在作為通知方法上面添加通知類(lèi)型注解,使用切入點(diǎn)表達(dá)式配置。
/**
* 增強(qiáng)類(lèi)
*/
@Component
@Aspect //生成代理對(duì)象
public class UserProxy {
/**
* 前置通知邏輯
*/
//@Before注解表示其作為前置通知
//切入點(diǎn)表達(dá)式指定該通知為哪個(gè)類(lèi)的哪個(gè)方法進(jìn)行增強(qiáng)。
@Before(value = "execution(* com.wyf.aopanno.User.add(..))")
public void before(){
System.out.println("before*****");
}
}測(cè)試代碼如下:
@Test
public void TestAop(){
//1. 加載spring 配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("beanAop1.xml");
//得到對(duì)象
User user = context.getBean("user",User.class);
user.add();
}執(zhí)行結(jié)果:

前文只給出了前置通知@Before的代碼,下面給出所有5種通知的示例代碼,對(duì)上例的所有通知情況進(jìn)行補(bǔ)全。
/**
* 增強(qiáng)類(lèi)
*/
@Component
@Aspect //生成代理對(duì)象
public class UserProxy {
/**
* 前置通知邏輯
*/
//@Before注解表示其作為前置通知
//切入點(diǎn)表達(dá)式指定該通知為哪個(gè)類(lèi)的哪個(gè)方法進(jìn)行增強(qiáng)。
@Before(value = "execution(* com.wyf.aopanno.User.add(..))")
public void before(){
System.out.println("before*****");
}
/**
* 后置通知(返回通知)
*/
@AfterReturning(value = "execution(* com.wyf.aopanno.User.add(..))")
public void afterReturning(){
System.out.println("afterReturning*****");
}
/**
* 最終通知,有異常也會(huì)執(zhí)行
*/
@After(value = "execution(* com.wyf.aopanno.User.add(..))")
public void after(){
System.out.println("after*****");
}
/**
* 異常通知
*/
@AfterThrowing(value = "execution(* com.wyf.aopanno.User.add(..))")
public void afterthrowing(){
System.out.println("afterthrowing*****");
}
/**
* 環(huán)繞通知,在方法之前和之后均通知
*/
@Around(value = "execution(* com.wyf.aopanno.User.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("環(huán)繞之前*****");
//執(zhí)行被增強(qiáng)的方法
proceedingJoinPoint.proceed();
System.out.println("環(huán)繞之后*****");
}代碼執(zhí)行結(jié)果如下:

其中,after是在被增強(qiáng)方法之后執(zhí)行,而afterreturning是在方法返回值之后執(zhí)行。
2.3 相同切入點(diǎn)的抽取
在上例種,我們編寫(xiě)了被增強(qiáng)方放的5種通知,分別為前置通知、后置通知、異常通知、最終通知以及環(huán)繞通知。我們?cè)诰帉?xiě)增強(qiáng)類(lèi)時(shí),在通知注解中通過(guò)切入點(diǎn)表達(dá)式,指明了通知要對(duì)哪個(gè)類(lèi)的哪個(gè)方法進(jìn)行增強(qiáng)。但是,我們會(huì)發(fā)現(xiàn),我們對(duì)同一方法進(jìn)行增強(qiáng)時(shí),其切入點(diǎn)表達(dá)式是相同的,為了避免重復(fù),我們可以相同切入點(diǎn)進(jìn)行抽取。
下例中,我們將相同切入點(diǎn)用@Pointcut進(jìn)行了抽取,代碼如下所示:
//對(duì)相同切入點(diǎn)進(jìn)行抽取
@Pointcut(value = "execution(* com.wyf.aopanno.User.add(..))")
public void pointdemo(){
}當(dāng)我們?cè)谄渌胤揭迷撉腥朦c(diǎn)表達(dá)式時(shí),直接用其方法名稱(chēng)就可以。
如下所示:
@Component
@Aspect
@Order(2)
public class PersonProxy {
//前置通知
@Before(value = "execution(* com.wyf.aopanno.User.add(..))")
public void before(){
System.out.println("Person Before****");
}
}2.4 多個(gè)增強(qiáng)類(lèi)對(duì)同一個(gè)方法進(jìn)行增強(qiáng),設(shè)置增強(qiáng)類(lèi)優(yōu)先級(jí)
假如我們現(xiàn)在又有一個(gè)增強(qiáng)類(lèi),其中也包含一個(gè)before()方法,也對(duì)被增強(qiáng)類(lèi)User的add()方法進(jìn)行前置增強(qiáng)。那么我們?nèi)绾卧O(shè)置其增強(qiáng)的順序呢?
我們通過(guò)在增強(qiáng)類(lèi)上面添加注解 @Order(數(shù)字類(lèi)型值),數(shù)字類(lèi)型值越小優(yōu)先級(jí)越高 ,來(lái)保證增強(qiáng)的順尋,代碼如下:
@Component
@Aspect
@Order(2)
public class PersonProxy {
//前置通知
@Before(value = "execution(* com.wyf.aopanno.User.add(..))")
public void before(){
System.out.println("Person Before****");
}
}三、結(jié)束
本文主要介紹了AOP關(guān)鍵子以及基于注解的AOP操作,通過(guò)一個(gè)實(shí)例,介紹了AOP的前置通知、后置通知、異常通知、最終通知以及環(huán)繞通知。
本篇文章就到這里了,希望能夠給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
springboot手動(dòng)動(dòng)態(tài)注入controller和service方式
這篇文章主要介紹了springboot手動(dòng)動(dòng)態(tài)注入controller和service方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03
Android開(kāi)發(fā)Kotlin實(shí)現(xiàn)圓弧計(jì)步器示例詳解
這篇文章主要為大家介紹了Android開(kāi)發(fā)Kotlin繪制圓弧計(jì)步器示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06
Java發(fā)送https請(qǐng)求并跳過(guò)ssl證書(shū)驗(yàn)證方法
最近在負(fù)責(zé)一個(gè)對(duì)接第三方服務(wù)的事情,在對(duì)接期間因?yàn)榈谌椒?wù)為https的請(qǐng)求,這篇文章主要給大家介紹了關(guān)于Java發(fā)送https請(qǐng)求并跳過(guò)ssl證書(shū)驗(yàn)證的相關(guān)資料,需要的朋友可以參考下2023-11-11
java中Hibernate面試知識(shí)點(diǎn)整理
在本篇文章里小編給大家整理的是一篇關(guān)于java中Hibernate面試知識(shí)點(diǎn)整理內(nèi)容,有興趣的朋友們可以學(xué)習(xí)參考下。2021-01-01
Java類(lèi)的加載時(shí)機(jī)與過(guò)程
這篇文章主要介紹了Java類(lèi)的加載時(shí)機(jī)與過(guò)程,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2021-12-12
HttpClient實(shí)現(xiàn)調(diào)用外部項(xiàng)目接口工具類(lèi)的示例
下面小編就為大家?guī)?lái)一篇HttpClient實(shí)現(xiàn)調(diào)用外部項(xiàng)目接口工具類(lèi)的示例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-10-10
IntelliJ IDEA打開(kāi)多個(gè)Maven的module且相互調(diào)用代碼的方法
這篇文章主要介紹了IntelliJ IDEA打開(kāi)多個(gè)Maven的module且相互調(diào)用代碼的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-02-02

