一.什么是AOP
AOP?為?Aspect Oriented Programming?的縮寫(xiě),意為:面向切面編程,通過(guò)預(yù)編譯方式和運(yùn)行期動(dòng)態(tài)代理實(shí)現(xiàn)程序功能的統(tǒng)一維護(hù)的一種技術(shù)。AOP?是?OOP(面向?qū)ο缶幊蹋┑难永m(xù),是軟件開(kāi)發(fā)中的一個(gè)熱點(diǎn),也是?Spring?框架中的一個(gè)重要內(nèi)容,是函數(shù)式編程的一種衍生范型。利用?AOP?可以對(duì)業(yè)務(wù)邏輯的各個(gè)部分進(jìn)行隔離,從而使得業(yè)務(wù)邏輯各部分之間的耦合度降低,使用AOP進(jìn)行編程,可以降低代碼的侵入性,提高程序的可重用性,同時(shí)提高了開(kāi)發(fā)的效率。
簡(jiǎn)單來(lái)說(shuō),aop是一種是一種思想和和規(guī)范,通過(guò)選擇不同方法(可以在不同類(lèi))在不同時(shí)機(jī)(方法執(zhí)行前、執(zhí)行后、返回前后、拋出異常后...),對(duì)選擇的方法統(tǒng)一添加處理邏輯。
springAOP是對(duì)AOP思想的一種具體實(shí)現(xiàn),但是它只實(shí)現(xiàn)了對(duì)方法的增強(qiáng),沒(méi)有實(shí)現(xiàn)對(duì)屬性的增強(qiáng)
二.AOP的典型應(yīng)用場(chǎng)景
- 統(tǒng)一的日志記錄?
- 統(tǒng)一的方法執(zhí)行時(shí)間統(tǒng)計(jì)
- 統(tǒng)一的返回格式設(shè)置
- 統(tǒng)一的異常處理
- 事務(wù)的開(kāi)啟和提交
三.關(guān)于AOP的核心知識(shí)點(diǎn)
切面類(lèi):定義一個(gè)類(lèi)來(lái)組織方法增強(qiáng)的邏輯(內(nèi)部由切點(diǎn)和通知組成)
切點(diǎn)(PointCut):定義要增強(qiáng)的方法(確定增強(qiáng)的范圍)
Pointcut 的作用就是提供一組規(guī)則(使用 AspectJ pointcut expression language 來(lái)描述)來(lái)匹配 Join Point,給滿(mǎn)足規(guī)則的 Join Point 添加 Advice通知:定義方法增強(qiáng)的實(shí)現(xiàn)邏輯(定義何時(shí)增強(qiáng)以及如何增強(qiáng))
定義了切面是什么,何時(shí)使用,其描述了切面要完成的工作,還解決何時(shí)執(zhí)行這個(gè)工作的問(wèn)題。
切點(diǎn)表達(dá)式說(shuō)明
AspectJ 支持三種通配符:* :匹配任意字符,只匹配一個(gè)元素(包,類(lèi),或方法,方法參數(shù))。* .. :匹配任意字符,可以匹配多個(gè)元素 ,在表示類(lèi)時(shí),必須和 * 聯(lián)合使用。+ :表示按照類(lèi)型匹配指定類(lèi)的所有類(lèi),必須跟在類(lèi)名后面,如 com.cad.Car+ , 表示繼承該類(lèi)的所有子類(lèi)包括本身。切點(diǎn)表達(dá)式由切點(diǎn)函數(shù)組成,其中 execution() 是最常用的切點(diǎn)函數(shù),用來(lái)匹配方法,語(yǔ)法為:execution(< 修飾符 >< 返回類(lèi)型 >< 包 . 類(lèi) . 方法 ( 參數(shù) )>< 異常 >)修飾符 和 異常 可以省略。表達(dá)式示例![]()
定義相關(guān)通知
通知定義的是被攔截的方法具體要執(zhí)行的業(yè)務(wù),比如用戶(hù)登錄權(quán)限驗(yàn)證方法就是具體要執(zhí)行的業(yè)務(wù)。Spring AOP 中,可以在方法上使用以下注解,會(huì)設(shè)置方法為通知方法,在滿(mǎn)足條件后會(huì)通知本方法進(jìn)行調(diào)用:前置通知使用@Before:通知方法會(huì)在目標(biāo)方法調(diào)用之前執(zhí)行。后置通知使用@After:通知方法會(huì)在目標(biāo)方法返回或者拋出異常后調(diào)用。返回之后通知使用@AfterReturning:通知方法會(huì)在目標(biāo)方法返回后調(diào)用。拋異常后通知使用@AfterThrowing:通知方法會(huì)在目標(biāo)方法拋出異常后調(diào)用。環(huán)繞通知使用@Around:通知包裹了被通知的方法,在被通知的方法通知之前和調(diào)用之后執(zhí)行自定義 的行為。
四.AOP的實(shí)現(xiàn)原理

對(duì)于使用JDK實(shí)現(xiàn)的動(dòng)態(tài)代理:代理類(lèi)和被代理類(lèi)要實(shí)現(xiàn)同一個(gè)接口,在實(shí)現(xiàn)的過(guò)程中會(huì)使用到的?api如下:Invocationhandler,proxy.newProxyInstance對(duì)于基于GCLIB實(shí)現(xiàn)的動(dòng)態(tài)代理:繼承原始類(lèi)(原始類(lèi)不能被final修飾),是專(zhuān)門(mén)生成代理類(lèi)的第三方框架,是基于asm字節(jié)碼框架生成的JDK 和 CGLIB 的區(qū)別1. JDK 實(shí)現(xiàn),要求被代理類(lèi)必須實(shí)現(xiàn)接口,之后是通過(guò) InvocationHandler 及 Proxy,在運(yùn)行時(shí)動(dòng)態(tài)的在 內(nèi)存中生成了代理類(lèi)對(duì)象,該代理對(duì)象是通過(guò)實(shí)現(xiàn)同樣的接口實(shí)現(xiàn)(類(lèi)似靜態(tài)代理接口實(shí)現(xiàn)的方式), 只是該代理類(lèi)是在運(yùn)行期時(shí),動(dòng)態(tài)的織入統(tǒng)一的業(yè)務(wù)邏輯字節(jié)碼來(lái)完成。2. CGLIB 實(shí)現(xiàn),被代理類(lèi)可以不實(shí)現(xiàn)接口,是通過(guò)繼承被代理類(lèi),在運(yùn)行時(shí)動(dòng)態(tài)的生成代理類(lèi)對(duì)象。
五.AOP的實(shí)現(xiàn)方式
JDK和GCLIB實(shí)現(xiàn)的代理類(lèi)如下:
JDK
import org.example.demo.service.AliPayService;
import org.example.demo.service.PayService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//動(dòng)態(tài)代理:使用JDK提供的api(InvocationHandler、Proxy實(shí)現(xiàn)),此種方式實(shí)現(xiàn),要求被代理類(lèi)必須實(shí)現(xiàn)接口
public class PayServiceJDKInvocationHandler implements InvocationHandler {
//目標(biāo)對(duì)象即就是被代理對(duì)象
private Object target;
public PayServiceJDKInvocationHandler(Object target) {
this.target = target;
}
//proxy代理對(duì)象
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws
Throwable {
//1.安全檢查
System.out.println("安全檢查");
//2.記錄日志
System.out.println("記錄日志");
//3.時(shí)間統(tǒng)計(jì)開(kāi)始
System.out.println("記錄開(kāi)始時(shí)間");
//通過(guò)反射調(diào)用被代理類(lèi)的方法
Object retVal = method.invoke(target, args);
//4.時(shí)間統(tǒng)計(jì)結(jié)束
System.out.println("記錄結(jié)束時(shí)間");
return retVal;
}
public static void main(String[] args) {
PayService target = new AliPayService();
//方法調(diào)用處理器
InvocationHandler handler =
new PayServiceJDKInvocationHandler(target);
//創(chuàng)建一個(gè)代理類(lèi):通過(guò)被代理類(lèi)、被代理實(shí)現(xiàn)的接口、方法調(diào)用處理器來(lái)創(chuàng)建
PayService proxy = (PayService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
new Class[]{PayService.class},
handler
);
proxy.pay();
}
}
GCLIB
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.example.demo.service.AliPayService;
import org.example.demo.service.PayService;
import java.lang.reflect.Method;
public class PayServiceCGLIBInterceptor implements MethodInterceptor {
//被代理對(duì)象
private Object target;
public PayServiceCGLIBInterceptor(Object target) {
this.target = target;
}
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy
methodProxy) throws Throwable {
//1.安全檢查
System.out.println("安全檢查");
//2.記錄日志
System.out.println("記錄日志");
//3.時(shí)間統(tǒng)計(jì)開(kāi)始
System.out.println("記錄開(kāi)始時(shí)間");
//通過(guò)cglib的代理方法調(diào)用
Object retVal = methodProxy.invoke(target, args);
//4.時(shí)間統(tǒng)計(jì)結(jié)束
System.out.println("記錄結(jié)束時(shí)間");
return retVal;
}
public static void main(String[] args) {
PayService target = new AliPayService();
PayService proxy = (PayService) Enhancer.create(target.getClass(), new
PayServiceCGLIBInterceptor(target));
proxy.pay();
}
}
?
六.AOP的使用
SpringAOP在使用時(shí)主要有以下兩種使用方式:
①使用aspectj風(fēng)格的注解進(jìn)行開(kāi)發(fā):
添加依賴(lài)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
定義通知時(shí)機(jī)和通知邏輯
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* @author tongchen
* @create 2023-05-08 11:14
*/
@Aspect
//將配置加載到容器中
@Component
public class AopConfig {
//定義切點(diǎn)
//包后面一定要有一個(gè)空格
@Pointcut("execution(* com.ljl..service.*Service.*(..))")
public void pointCut(){
}
@Around("pointCut()")
public Object around(ProceedingJoinPoint joinPoint){
//pointcut匹配的方法
try {
Long start=System.currentTimeMillis();
Object proceed = joinPoint.proceed();
Long end=System.currentTimeMillis();
System.out.println("方法運(yùn)行的時(shí)間:"+(end-start));
} catch (Throwable e) {
throw new RuntimeException(e);
}
return null;
}
}
定義切點(diǎn)方法文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-450352.html
import org.springframework.stereotype.Service;
import java.util.ArrayList;
/**
* @author tongchen
* @create 2023-05-08 11:13
*/
@Service
public class AspectService {
public Object timeTest(){
//模擬獲取數(shù)據(jù)庫(kù)中的數(shù)據(jù)
return new ArrayList<Integer>();
}
}
②定義一個(gè)類(lèi),實(shí)現(xiàn)MethodInteceptor文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-450352.html
到了這里,關(guān)于AOP(面向切面編程)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!