1、場(chǎng)景模擬
搭建子模塊:spring6-aop
1.1、聲明接口
聲明計(jì)算器接口Calculator,包含加減乘除的抽象方法
public interface Calculator {
int add(int i, int j);
int sub(int i, int j);
int mul(int i, int j);
int div(int i, int j);
}
1.2、創(chuàng)建實(shí)現(xiàn)類
public class CalculatorImpl implements Calculator {
@Override
public int add(int i, int j) {
int result = i + j;
System.out.println("方法內(nèi)部 result = " + result);
return result;
}
@Override
public int sub(int i, int j) {
int result = i - j;
System.out.println("方法內(nèi)部 result = " + result);
return result;
}
@Override
public int mul(int i, int j) {
int result = i * j;
System.out.println("方法內(nèi)部 result = " + result);
return result;
}
@Override
public int div(int i, int j) {
int result = i / j;
System.out.println("方法內(nèi)部 result = " + result);
return result;
}
}
1.3、創(chuàng)建帶日志功能的實(shí)現(xiàn)類
public class CalculatorLogImpl implements Calculator {
@Override
public int add(int i, int j) {
System.out.println("[日志] add 方法開始了,參數(shù)是:" + i + "," + j);
int result = i + j;
System.out.println("方法內(nèi)部 result = " + result);
System.out.println("[日志] add 方法結(jié)束了,結(jié)果是:" + result);
return result;
}
@Override
public int sub(int i, int j) {
System.out.println("[日志] sub 方法開始了,參數(shù)是:" + i + "," + j);
int result = i - j;
System.out.println("方法內(nèi)部 result = " + result);
System.out.println("[日志] sub 方法結(jié)束了,結(jié)果是:" + result);
return result;
}
@Override
public int mul(int i, int j) {
System.out.println("[日志] mul 方法開始了,參數(shù)是:" + i + "," + j);
int result = i * j;
System.out.println("方法內(nèi)部 result = " + result);
System.out.println("[日志] mul 方法結(jié)束了,結(jié)果是:" + result);
return result;
}
@Override
public int div(int i, int j) {
System.out.println("[日志] div 方法開始了,參數(shù)是:" + i + "," + j);
int result = i / j;
System.out.println("方法內(nèi)部 result = " + result);
System.out.println("[日志] div 方法結(jié)束了,結(jié)果是:" + result);
return result;
}
}
1.4、提出問題
①現(xiàn)有代碼缺陷
針對(duì)帶日志功能的實(shí)現(xiàn)類,我們發(fā)現(xiàn)有如下缺陷:
- 對(duì)核心業(yè)務(wù)功能有干擾,導(dǎo)致程序員在開發(fā)核心業(yè)務(wù)功能時(shí)分散了精力
- 附加功能分散在各個(gè)業(yè)務(wù)功能方法中,不利于統(tǒng)一維護(hù)
②解決思路
解決這兩個(gè)問題,核心就是:解耦。我們需要把附加功能從業(yè)務(wù)功能代碼中抽取出來。
③困難
解決問題的困難:要抽取的代碼在方法內(nèi)部,靠以前把子類中的重復(fù)代碼抽取到父類的方式?jīng)]法解決。所以需要引入新的技術(shù)。
2、代理模式
2.1、概念
①介紹
二十三種設(shè)計(jì)模式中的一種,屬于結(jié)構(gòu)型模式。它的作用就是通過提供一個(gè)代理類,讓我們?cè)谡{(diào)用目標(biāo)方法的時(shí)候,不再是直接對(duì)目標(biāo)方法進(jìn)行調(diào)用,而是通過代理類間接調(diào)用。讓不屬于目標(biāo)方法核心邏輯的代碼從目標(biāo)方法中剝離出來——解耦。調(diào)用目標(biāo)方法時(shí)先調(diào)用代理對(duì)象的方法,減少對(duì)目標(biāo)方法的調(diào)用和打擾,同時(shí)讓附加功能能夠集中在一起也有利于統(tǒng)一維護(hù)。
②生活中的代理
- 廣告商找大明星拍廣告需要經(jīng)過經(jīng)紀(jì)人
- 合作伙伴找大老板談合作要約見面時(shí)間需要經(jīng)過秘書
- 房產(chǎn)中介是買賣雙方的代理
③相關(guān)術(shù)語
- 代理:將非核心邏輯剝離出來以后,封裝這些非核心邏輯的類、對(duì)象、方法。
- 目標(biāo):被代理“套用”了非核心邏輯代碼的類、對(duì)象、方法。
2.2、靜態(tài)代理
創(chuàng)建靜態(tài)代理類:
public class CalculatorStaticProxy implements Calculator {
// 將被代理的目標(biāo)對(duì)象聲明為成員變量
private Calculator target;
public CalculatorStaticProxy(Calculator target) {
this.target = target;
}
@Override
public int add(int i, int j) {
// 附加功能由代理類中的代理方法來實(shí)現(xiàn)
System.out.println("[日志] add 方法開始了,參數(shù)是:" + i + "," + j);
// 通過目標(biāo)對(duì)象來實(shí)現(xiàn)核心業(yè)務(wù)邏輯
int addResult = target.add(i, j);
System.out.println("[日志] add 方法結(jié)束了,結(jié)果是:" + addResult);
return addResult;
}
}
靜態(tài)代理確實(shí)實(shí)現(xiàn)了解耦,但是由于代碼都寫死了,完全不具備任何的靈活性。就拿日志功能來說,將來其他地方也需要附加日志,那還得再聲明更多個(gè)靜態(tài)代理類,那就產(chǎn)生了大量重復(fù)的代碼,日志功能還是分散的,沒有統(tǒng)一管理。
提出進(jìn)一步的需求:將日志功能集中到一個(gè)代理類中,將來有任何日志需求,都通過這一個(gè)代理類來實(shí)現(xiàn)。這就需要使用動(dòng)態(tài)代理技術(shù)了。
2.3、動(dòng)態(tài)代理
生產(chǎn)代理對(duì)象的工廠類:
public class ProxyFactory {
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
public Object getProxy(){
/**
* newProxyInstance():創(chuàng)建一個(gè)代理實(shí)例
* 其中有三個(gè)參數(shù):
* 1、classLoader:加載動(dòng)態(tài)生成的代理類的類加載器
* 2、interfaces:目標(biāo)對(duì)象實(shí)現(xiàn)的所有接口的class對(duì)象所組成的數(shù)組
* 3、invocationHandler:設(shè)置代理對(duì)象實(shí)現(xiàn)目標(biāo)對(duì)象方法的過程,即代理類中如何重寫接口中的抽象方法
*/
ClassLoader classLoader = target.getClass().getClassLoader();
Class<?>[] interfaces = target.getClass().getInterfaces();
InvocationHandler invocationHandler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/**
* proxy:代理對(duì)象
* method:代理對(duì)象需要實(shí)現(xiàn)的方法,即其中需要重寫的方法
* args:method所對(duì)應(yīng)方法的參數(shù)
*/
Object result = null;
try {
System.out.println("[動(dòng)態(tài)代理][日志] "+method.getName()+",參數(shù):"+ Arrays.toString(args));
result = method.invoke(target, args);
System.out.println("[動(dòng)態(tài)代理][日志] "+method.getName()+",結(jié)果:"+ result);
} catch (Exception e) {
e.printStackTrace();
System.out.println("[動(dòng)態(tài)代理][日志] "+method.getName()+",異常:"+e.getMessage());
} finally {
System.out.println("[動(dòng)態(tài)代理][日志] "+method.getName()+",方法執(zhí)行完畢");
}
return result;
}
};
return Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
}
}
2.4、測(cè)試
@Test
public void testDynamicProxy(){
ProxyFactory factory = new ProxyFactory(new CalculatorLogImpl());
Calculator proxy = (Calculator) factory.getProxy();
proxy.div(1,0);
//proxy.div(1,1);
}
3、AOP概念及相關(guān)術(shù)語
3.1、概述
AOP(Aspect Oriented Programming)是一種設(shè)計(jì)思想,是軟件設(shè)計(jì)領(lǐng)域中的面向切面編程,它是面向?qū)ο缶幊痰囊环N補(bǔ)充和完善,它以通過預(yù)編譯方式和運(yùn)行期動(dòng)態(tài)代理方式實(shí)現(xiàn),在不修改源代碼的情況下,給程序動(dòng)態(tài)統(tǒng)一添加額外功能的一種技術(shù)。利用AOP可以對(duì)業(yè)務(wù)邏輯的各個(gè)部分進(jìn)行隔離,從而使得業(yè)務(wù)邏輯各部分之間的耦合度降低,提高程序的可重用性,同時(shí)提高了開發(fā)的效率。
3.2、相關(guān)術(shù)語
①橫切關(guān)注點(diǎn)
分散在每個(gè)各個(gè)模塊中解決同一樣的問題,如用戶驗(yàn)證、日志管理、事務(wù)處理、數(shù)據(jù)緩存都屬于橫切關(guān)注點(diǎn)。
從每個(gè)方法中抽取出來的同一類非核心業(yè)務(wù)。在同一個(gè)項(xiàng)目中,我們可以使用多個(gè)橫切關(guān)注點(diǎn)對(duì)相關(guān)方法進(jìn)行多個(gè)不同方面的增強(qiáng)。
這個(gè)概念不是語法層面的,而是根據(jù)附加功能的邏輯上的需要:有十個(gè)附加功能,就有十個(gè)橫切關(guān)注點(diǎn)。
②通知(增強(qiáng))
增強(qiáng),通俗說,就是你想要增強(qiáng)的功能,比如 安全,事務(wù),日志等。
每一個(gè)橫切關(guān)注點(diǎn)上要做的事情都需要寫一個(gè)方法來實(shí)現(xiàn),這樣的方法就叫通知方法。
- 前置通知:在被代理的目標(biāo)方法前執(zhí)行
- 返回通知:在被代理的目標(biāo)方法成功結(jié)束后執(zhí)行(壽終正寢)
- 異常通知:在被代理的目標(biāo)方法異常結(jié)束后執(zhí)行(死于非命)
- 后置通知:在被代理的目標(biāo)方法最終結(jié)束后執(zhí)行(蓋棺定論)
- 環(huán)繞通知:使用try…catch…finally結(jié)構(gòu)圍繞整個(gè)被代理的目標(biāo)方法,包括上面四種通知對(duì)應(yīng)的所有位置
③切面
封裝通知方法的類。
④目標(biāo)
被代理的目標(biāo)對(duì)象。
⑤代理
向目標(biāo)對(duì)象應(yīng)用通知之后創(chuàng)建的代理對(duì)象。
⑥連接點(diǎn)
這也是一個(gè)純邏輯概念,不是語法定義的。
把方法排成一排,每一個(gè)橫切位置看成x軸方向,把方法從上到下執(zhí)行的順序看成y軸,x軸和y軸的交叉點(diǎn)就是連接點(diǎn)。通俗說,就是spring允許你使用通知的地方
⑦切入點(diǎn)
定位連接點(diǎn)的方式。
每個(gè)類的方法中都包含多個(gè)連接點(diǎn),所以連接點(diǎn)是類中客觀存在的事物(從邏輯上來說)。
如果把連接點(diǎn)看作數(shù)據(jù)庫中的記錄,那么切入點(diǎn)就是查詢記錄的 SQL 語句。
Spring 的 AOP 技術(shù)可以通過切入點(diǎn)定位到特定的連接點(diǎn)。通俗說,要實(shí)際去增強(qiáng)的方法
切點(diǎn)通過 org.springframework.aop.Pointcut 接口進(jìn)行描述,它使用類和方法作為連接點(diǎn)的查詢條件。
3.3、作用
-
簡(jiǎn)化代碼:把方法中固定位置的重復(fù)的代碼抽取出來,讓被抽取的方法更專注于自己的核心功能,提高內(nèi)聚性。
-
代碼增強(qiáng):把特定的功能封裝到切面類中,看哪里有需要,就往上套,被套用了切面邏輯的方法就被切面給增強(qiáng)了。
4、基于注解的AOP
4.1、技術(shù)說明
- 動(dòng)態(tài)代理分為JDK動(dòng)態(tài)代理和cglib動(dòng)態(tài)代理
- 當(dāng)目標(biāo)類有接口的情況使用JDK動(dòng)態(tài)代理和cglib動(dòng)態(tài)代理,沒有接口時(shí)只能使用cglib動(dòng)態(tài)代理
- JDK動(dòng)態(tài)代理動(dòng)態(tài)生成的代理類會(huì)在com.sun.proxy包下,類名為$proxy1,和目標(biāo)類實(shí)現(xiàn)相同的接口
- cglib動(dòng)態(tài)代理動(dòng)態(tài)生成的代理類會(huì)和目標(biāo)在在相同的包下,會(huì)繼承目標(biāo)類
- 動(dòng)態(tài)代理(InvocationHandler):JDK原生的實(shí)現(xiàn)方式,需要被代理的目標(biāo)類必須實(shí)現(xiàn)接口。因?yàn)檫@個(gè)技術(shù)要求代理對(duì)象和目標(biāo)對(duì)象實(shí)現(xiàn)同樣的接口(兄弟兩個(gè)拜把子模式)。
- cglib:通過繼承被代理的目標(biāo)類(認(rèn)干爹模式)實(shí)現(xiàn)代理,所以不需要目標(biāo)類實(shí)現(xiàn)接口。
- AspectJ:是AOP思想的一種實(shí)現(xiàn)。本質(zhì)上是靜態(tài)代理,將代理邏輯“織入”被代理的目標(biāo)類編譯得到的字節(jié)碼文件,所以最終效果是動(dòng)態(tài)的。weaver就是織入器。Spring只是借用了AspectJ中的注解。
4.2、準(zhǔn)備工作
①添加依賴
在IOC所需依賴基礎(chǔ)上再加入下面依賴即可:
<dependencies>
<!--spring context依賴-->
<!--當(dāng)你引入Spring Context依賴之后,表示將Spring的基礎(chǔ)依賴引入了-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.2</version>
</dependency>
<!--spring aop依賴-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>6.0.2</version>
</dependency>
<!--spring aspects依賴-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>6.0.2</version>
</dependency>
<!--junit5測(cè)試-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.3.1</version>
</dependency>
<!--log4j2的依賴-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.19.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j2-impl</artifactId>
<version>2.19.0</version>
</dependency>
</dependencies>
②準(zhǔn)備被代理的目標(biāo)資源
接口:
public interface Calculator {
int add(int i, int j);
int sub(int i, int j);
int mul(int i, int j);
int div(int i, int j);
}
實(shí)現(xiàn)類:
@Component
public class CalculatorImpl implements Calculator {
@Override
public int add(int i, int j) {
int result = i + j;
System.out.println("方法內(nèi)部 result = " + result);
return result;
}
@Override
public int sub(int i, int j) {
int result = i - j;
System.out.println("方法內(nèi)部 result = " + result);
return result;
}
@Override
public int mul(int i, int j) {
int result = i * j;
System.out.println("方法內(nèi)部 result = " + result);
return result;
}
@Override
public int div(int i, int j) {
int result = i / j;
System.out.println("方法內(nèi)部 result = " + result);
return result;
}
}
4.3、創(chuàng)建切面類并配置
// @Aspect表示這個(gè)類是一個(gè)切面類
@Aspect
// @Component注解保證這個(gè)切面類能夠放入IOC容器
@Component
public class LogAspect {
@Before("execution(public int com.atguigu.aop.annotation.CalculatorImpl.*(..))")
public void beforeMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
String args = Arrays.toString(joinPoint.getArgs());
System.out.println("Logger-->前置通知,方法名:"+methodName+",參數(shù):"+args);
}
@After("execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))")
public void afterMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
System.out.println("Logger-->后置通知,方法名:"+methodName);
}
@AfterReturning(value = "execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))", returning = "result")
public void afterReturningMethod(JoinPoint joinPoint, Object result){
String methodName = joinPoint.getSignature().getName();
System.out.println("Logger-->返回通知,方法名:"+methodName+",結(jié)果:"+result);
}
@AfterThrowing(value = "execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))", throwing = "ex")
public void afterThrowingMethod(JoinPoint joinPoint, Throwable ex){
String methodName = joinPoint.getSignature().getName();
System.out.println("Logger-->異常通知,方法名:"+methodName+",異常:"+ex);
}
@Around("execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))")
public Object aroundMethod(ProceedingJoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
String args = Arrays.toString(joinPoint.getArgs());
Object result = null;
try {
System.out.println("環(huán)繞通知-->目標(biāo)對(duì)象方法執(zhí)行之前");
//目標(biāo)對(duì)象(連接點(diǎn))方法的執(zhí)行
result = joinPoint.proceed();
System.out.println("環(huán)繞通知-->目標(biāo)對(duì)象方法返回值之后");
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("環(huán)繞通知-->目標(biāo)對(duì)象方法出現(xiàn)異常時(shí)");
} finally {
System.out.println("環(huán)繞通知-->目標(biāo)對(duì)象方法執(zhí)行完畢");
}
return result;
}
}
在Spring的配置文件中配置:
<?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">
<!--
基于注解的AOP的實(shí)現(xiàn):
1、將目標(biāo)對(duì)象和切面交給IOC容器管理(注解+掃描)
2、開啟AspectJ的自動(dòng)代理,為目標(biāo)對(duì)象自動(dòng)生成代理
3、將切面類通過注解@Aspect標(biāo)識(shí)
-->
<context:component-scan base-package="com.atguigu.aop.annotation"></context:component-scan>
<aop:aspectj-autoproxy />
</beans>
執(zhí)行測(cè)試:
public class CalculatorTest {
private Logger logger = LoggerFactory.getLogger(CalculatorTest.class);
@Test
public void testAdd(){
ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
Calculator calculator = ac.getBean( Calculator.class);
int add = calculator.add(1, 1);
logger.info("執(zhí)行成功:"+add);
}
}
執(zhí)行結(jié)果:
4.4、各種通知
- 前置通知:使用@Before注解標(biāo)識(shí),在被代理的目標(biāo)方法前執(zhí)行
- 返回通知:使用@AfterReturning注解標(biāo)識(shí),在被代理的目標(biāo)方法成功結(jié)束后執(zhí)行(壽終正寢)
- 異常通知:使用@AfterThrowing注解標(biāo)識(shí),在被代理的目標(biāo)方法異常結(jié)束后執(zhí)行(死于非命)
- 后置通知:使用@After注解標(biāo)識(shí),在被代理的目標(biāo)方法最終結(jié)束后執(zhí)行(蓋棺定論)
- 環(huán)繞通知:使用@Around注解標(biāo)識(shí),使用try…catch…finally結(jié)構(gòu)圍繞整個(gè)被代理的目標(biāo)方法,包括上面四種通知對(duì)應(yīng)的所有位置
各種通知的執(zhí)行順序:
- Spring版本5.3.x以前:
- 前置通知
- 目標(biāo)操作
- 后置通知
- 返回通知或異常通知
- Spring版本5.3.x以后:
- 前置通知
- 目標(biāo)操作
- 返回通知或異常通知
- 后置通知
4.5、切入點(diǎn)表達(dá)式語法
①作用
②語法細(xì)節(jié)
-
用*號(hào)代替“權(quán)限修飾符”和“返回值”部分表示“權(quán)限修飾符”和“返回值”不限
-
在包名的部分,一個(gè)“*”號(hào)只能代表包的層次結(jié)構(gòu)中的一層,表示這一層是任意的。
- 例如:*.Hello匹配com.Hello,不匹配com.atguigu.Hello
-
在包名的部分,使用“*…”表示包名任意、包的層次深度任意
-
在類名的部分,類名部分整體用*號(hào)代替,表示類名任意
-
在類名的部分,可以使用*號(hào)代替類名的一部分
- 例如:*Service匹配所有名稱以Service結(jié)尾的類或接口
-
在方法名部分,可以使用*號(hào)表示方法名任意
-
在方法名部分,可以使用*號(hào)代替方法名的一部分
- 例如:*Operation匹配所有方法名以O(shè)peration結(jié)尾的方法
-
在方法參數(shù)列表部分,使用(…)表示參數(shù)列表任意
-
在方法參數(shù)列表部分,使用(int,…)表示參數(shù)列表以一個(gè)int類型的參數(shù)開頭
-
在方法參數(shù)列表部分,基本數(shù)據(jù)類型和對(duì)應(yīng)的包裝類型是不一樣的
- 切入點(diǎn)表達(dá)式中使用 int 和實(shí)際方法中 Integer 是不匹配的
-
在方法返回值部分,如果想要明確指定一個(gè)返回值類型,那么必須同時(shí)寫明權(quán)限修飾符
- 例如:execution(public int …Service.(…, int)) 正確
例如:execution( int *…Service.(…, int)) 錯(cuò)誤
- 例如:execution(public int …Service.(…, int)) 正確
4.6、重用切入點(diǎn)表達(dá)式
①聲明
@Pointcut("execution(* com.atguigu.aop.annotation.*.*(..))")
public void pointCut(){}
②在同一個(gè)切面中使用
@Before("pointCut()")
public void beforeMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
String args = Arrays.toString(joinPoint.getArgs());
System.out.println("Logger-->前置通知,方法名:"+methodName+",參數(shù):"+args);
}
③在不同切面中使用
@Before("com.atguigu.aop.CommonPointCut.pointCut()")
public void beforeMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
String args = Arrays.toString(joinPoint.getArgs());
System.out.println("Logger-->前置通知,方法名:"+methodName+",參數(shù):"+args);
}
4.7、獲取通知的相關(guān)信息
①獲取連接點(diǎn)信息
獲取連接點(diǎn)信息可以在通知方法的參數(shù)位置設(shè)置JoinPoint類型的形參
@Before("execution(public int com.atguigu.aop.annotation.CalculatorImpl.*(..))")
public void beforeMethod(JoinPoint joinPoint){
//獲取連接點(diǎn)的簽名信息
String methodName = joinPoint.getSignature().getName();
//獲取目標(biāo)方法到的實(shí)參信息
String args = Arrays.toString(joinPoint.getArgs());
System.out.println("Logger-->前置通知,方法名:"+methodName+",參數(shù):"+args);
}
②獲取目標(biāo)方法的返回值
@AfterReturning中的屬性returning,用來將通知方法的某個(gè)形參,接收目標(biāo)方法的返回值
@AfterReturning(value = "execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))", returning = "result")
public void afterReturningMethod(JoinPoint joinPoint, Object result){
String methodName = joinPoint.getSignature().getName();
System.out.println("Logger-->返回通知,方法名:"+methodName+",結(jié)果:"+result);
}
③獲取目標(biāo)方法的異常
@AfterThrowing中的屬性throwing,用來將通知方法的某個(gè)形參,接收目標(biāo)方法的異常
@AfterThrowing(value = "execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))", throwing = "ex")
public void afterThrowingMethod(JoinPoint joinPoint, Throwable ex){
String methodName = joinPoint.getSignature().getName();
System.out.println("Logger-->異常通知,方法名:"+methodName+",異常:"+ex);
}
4.8、環(huán)繞通知
@Around("execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))")
public Object aroundMethod(ProceedingJoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
String args = Arrays.toString(joinPoint.getArgs());
Object result = null;
try {
System.out.println("環(huán)繞通知-->目標(biāo)對(duì)象方法執(zhí)行之前");
//目標(biāo)方法的執(zhí)行,目標(biāo)方法的返回值一定要返回給外界調(diào)用者
result = joinPoint.proceed();
System.out.println("環(huán)繞通知-->目標(biāo)對(duì)象方法返回值之后");
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("環(huán)繞通知-->目標(biāo)對(duì)象方法出現(xiàn)異常時(shí)");
} finally {
System.out.println("環(huán)繞通知-->目標(biāo)對(duì)象方法執(zhí)行完畢");
}
return result;
}
4.9、切面的優(yōu)先級(jí)
相同目標(biāo)方法上同時(shí)存在多個(gè)切面時(shí),切面的優(yōu)先級(jí)控制切面的內(nèi)外嵌套順序。
- 優(yōu)先級(jí)高的切面:外面
- 優(yōu)先級(jí)低的切面:里面
使用@Order注解可以控制切面的優(yōu)先級(jí):
- @Order(較小的數(shù)):優(yōu)先級(jí)高
- @Order(較大的數(shù)):優(yōu)先級(jí)低
文章來源:http://www.zghlxwxcb.cn/news/detail-714191.html
5、基于XML的AOP
5.1、準(zhǔn)備工作
參考基于注解的AOP環(huán)境文章來源地址http://www.zghlxwxcb.cn/news/detail-714191.html
5.2、實(shí)現(xiàn)
<context:component-scan base-package="com.atguigu.aop.xml"></context:component-scan>
<aop:config>
<!--配置切面類-->
<aop:aspect ref="loggerAspect">
<aop:pointcut id="pointCut"
expression="execution(* com.atguigu.aop.xml.CalculatorImpl.*(..))"/>
<aop:before method="beforeMethod" pointcut-ref="pointCut"></aop:before>
<aop:after method="afterMethod" pointcut-ref="pointCut"></aop:after>
<aop:after-returning method="afterReturningMethod" returning="result" pointcut-ref="pointCut"></aop:after-returning>
<aop:after-throwing method="afterThrowingMethod" throwing="ex" pointcut-ref="pointCut"></aop:after-throwing>
<aop:around method="aroundMethod" pointcut-ref="pointCut"></aop:around>
</aop:aspect>
</aop:config>
到了這里,關(guān)于spring6-AOP面向切面編程的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!