Spring AOP的概念與實(shí)現(xiàn)過程詳解
Aop
什么是Aop?
AOP就是面向切面編程,通過預(yù)編譯方式以及運(yùn)行期間的動(dòng)態(tài)代理技術(shù)來實(shí)現(xiàn)程序的統(tǒng)一維護(hù)功能。
什么是切面,我理解的切面就是兩個(gè)方法之間,兩個(gè)對(duì)象之間,兩個(gè)模塊之間就是一個(gè)切面。假設(shè)在兩個(gè)模塊之間需要共同執(zhí)行一系列操作,并且最后將這一系列操作注入到兩個(gè)模塊之間的指定位置。此時(shí)這一系列操作就是切面,注入這些操作的位置稱之為切點(diǎn)。
舉例:公司員工上班
A員工上班需要在前臺(tái)進(jìn)行打卡,同樣的B員工…其他員工都需要在前臺(tái)打卡,那么如果為每一位員工提供單獨(dú)的打卡通道就有些過于浪費(fèi)資源。像這樣
于是,在前臺(tái)這個(gè)位置設(shè)置接口,聲明公共的打卡方法,所有員工共同通過該接口進(jìn)行打卡,那么在打卡時(shí)的一系列校驗(yàn)或者記錄的整個(gè)操作就可以被稱之為切面,前臺(tái)這個(gè)空間位置就被稱之為切點(diǎn),如下圖所示
aop主要作用就是進(jìn)行日志記錄、事務(wù)/異常處理等功能以便更好地維護(hù)開發(fā),主要目的就是將這些行為從項(xiàng)目的業(yè)務(wù)邏輯代碼中分離出來并且降低各個(gè)業(yè)務(wù)邏輯模塊之間的耦合度。保證在執(zhí)行aop操作的同時(shí)不會(huì)影響到項(xiàng)目的業(yè)務(wù)邏輯代碼
幾個(gè)概念
Aspect
:切面 Aspect中會(huì)包含一些pointCut切入點(diǎn)以及一些Advice
Advice
:通知 切面需要完成的工作,通過before、after、around來區(qū)別是在連接點(diǎn)之前或者之后 或者環(huán)繞
Target
:目標(biāo) 目標(biāo)對(duì)象,該對(duì)象會(huì)被織入advice
PointCut
:切點(diǎn) 即切面通知執(zhí)行的地點(diǎn) advice將會(huì)在這里發(fā)生
JointPoint
:連接點(diǎn) 所有方法的執(zhí)行點(diǎn)
PointCut用來修飾JointPoint,PointCut是advice執(zhí)行的點(diǎn),而JointPoint表示所有方法的執(zhí)行點(diǎn),通過PointCut可以確定哪些JointPoint是可以被織入的點(diǎn)
我們通常不希望advice會(huì)在所有的JointPoint點(diǎn)執(zhí)行,PointCut的作用就是可以進(jìn)行校驗(yàn)來更精準(zhǔn)的匹配執(zhí)行點(diǎn)。簡(jiǎn)單概括就是Jointpoint可以執(zhí)行但未必執(zhí)行,只有PointCut匹配到了JointPoint才可以在該點(diǎn)執(zhí)行
Aspect切面可以理解為PointCut+Advice
使用AOP織入導(dǎo)入包
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.7</version> <scope>runtime</scope> </dependency>
實(shí)現(xiàn)aop方式一
使用spring內(nèi)置的API接口
準(zhǔn)備:UserService、UserServiceImpl簡(jiǎn)單實(shí)現(xiàn)CRUD
在spring核心配置文件applicationContext.xml中配置aop:
<?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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="userServiceImpl" class="com.mount.service.UserServiceImpl"/> <bean id="log" class="com.mount.log.log"/> <bean id="afterLog" class="com.mount.log.AfterLog"/> <!-- 配置AOP --> <aop:config> <!-- 切入點(diǎn) expression表達(dá)式 表示從哪里開始執(zhí)行 --> <aop:pointcut id="pointcut" expression="execution(* com.mount.service.UserServiceImpl.*(..))"/> <!-- 執(zhí)行環(huán)繞增強(qiáng) --> <aop:advisor advice-ref="log" pointcut-ref="pointcut"/> <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/> </aop:config> </beans>
編寫執(zhí)行前后日志
// 執(zhí)行前 實(shí)現(xiàn)spring內(nèi)置接口MethodBeforeAdvice public class log implements MethodBeforeAdvice { public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println(target.getClass().getName()+"執(zhí)行了"+method.getName()+"方法"); } } // 執(zhí)行后 實(shí)現(xiàn)AfterReturningAdvice public class AfterLog implements AfterReturningAdvice { public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println(target.getClass().getName()+"調(diào)用了"+method.getName()+"方法"+"返回的結(jié)果為"+returnValue); } }
測(cè)試:
@Test public void test01(){ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); UserService bean = context.getBean("userServiceImpl", UserService.class); bean.insert(); }
實(shí)現(xiàn)aop方式二
使用自定義類,只需要在applicationContext.xml中重新配置aop,并且自己diy一個(gè)類即可,在配置時(shí)可以選擇任一方法為前置日志或后置日志即可
<!-- 配置AOP 方式二 --> <bean id="diy" class="com.mount.diyLog.diyLogImpl"/> <aop:config> <!-- 自定義切面 ref引入的類 --> <aop:aspect ref="diy"> <aop:pointcut id="pointcut" expression="execution(* com.mount.service.UserServiceImpl.*(..))"/> <aop:before method="before" pointcut-ref="pointcut"/> <aop:after method="after" pointcut-ref="pointcut"/> </aop:aspect> </aop:config>
diylog類
public class diyLog { public void before(){ System.out.println("===before方法執(zhí)行==="); } public void after(){ System.out.println("===after方法執(zhí)行==="); } }
注解實(shí)現(xiàn)aop
首先需要在applicationContext.xml文件中打開SpringAOP對(duì)注解的支持
<!-- SpringAop開啟注解支持 --> <aop:aspectj-autoproxy/> <!-- 映射自定義注解實(shí)現(xiàn)log類 --> <bean id="annoLog" class="com.mount.annoLog.annoLogImpl"/>
annoLog
// Aspect標(biāo)注該類是一個(gè)切面 @Aspect public class annoLogImpl { // 前置增強(qiáng) @Before("execution(* com.mount.service.UserServiceImpl.*(..))") public void before(){ System.out.println("---方法執(zhí)行前---"); } // 后置增強(qiáng) @After("execution(* com.mount.service.UserServiceImpl.*(..))") public void after(){ System.out.println("---方法執(zhí)行后---"); } // 環(huán)繞增強(qiáng) @Around("execution(* com.yuqu.dao.UserMapperImpl.*(..))") public Object around(ProceedingJoinPoint pjp) throws Throwable { System.out.println("環(huán)繞前"); Object proceed = pjp.proceed(); System.out.println("環(huán)繞后"); System.out.println("執(zhí)行信息:"+pjp.getSignature()); return proceed; } }
最終打印:
環(huán)繞前
---方法執(zhí)行前---
刪除成功!
---方法執(zhí)行后---
方法簽名Integer com.mount.service.UserService.delete()
方法執(zhí)行返回=1
環(huán)繞后
注意around環(huán)繞增強(qiáng),如果我們執(zhí)行的sql中是有返回值的話,那么必須顯式的將pjp.proceed();返回回去,否則在調(diào)用處將會(huì)無法獲取到結(jié)果集,報(bào)空指針異常
可以發(fā)現(xiàn),around環(huán)繞增強(qiáng)首先執(zhí)行,在執(zhí)行到joinPoint.proceed()
時(shí),會(huì)執(zhí)行對(duì)應(yīng)方法,執(zhí)行對(duì)應(yīng)方法的時(shí)候才會(huì)執(zhí)行前置或后置的其他增強(qiáng)操作
到此這篇關(guān)于Spring AOP的概念與實(shí)現(xiàn)過程詳解的文章就介紹到這了,更多相關(guān)Spring AOP概念內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Spring AOP與AspectJ的對(duì)比及應(yīng)用詳解
- Spring使用AOP完成統(tǒng)一結(jié)果封裝實(shí)例demo
- SpringBoot使用AOP與注解實(shí)現(xiàn)請(qǐng)求參數(shù)自動(dòng)填充流程詳解
- Spring AOP統(tǒng)一功能處理示例代碼
- Spring?AOP實(shí)現(xiàn)用戶登錄統(tǒng)一驗(yàn)證功能
- Spring AOP源碼深入分析
- Spring AOP如何自定義注解實(shí)現(xiàn)審計(jì)或日志記錄(完整代碼)
- Spring?AOP實(shí)現(xiàn)聲明式事務(wù)機(jī)制源碼解析
相關(guān)文章
IntelliJ IDEA中ajax開發(fā)實(shí)現(xiàn)分頁(yè)查詢示例
這篇文章主要介紹了IntelliJ IDEA中ajax開發(fā)實(shí)現(xiàn)分頁(yè)查詢,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-03-03Shiro在springboot中快速實(shí)現(xiàn)方法
Apache Shiro是一個(gè)Java的安全(權(quán)限)框架,可以容易的開發(fā)出足夠好的應(yīng)用,既可以在JavaEE中使用,也可以在JavaSE中使用,這篇文章主要介紹了Shiro在springboot中快速實(shí)現(xiàn),需要的朋友可以參考下2023-02-02idea自動(dòng)加載html、js而無需重啟進(jìn)程的操作
這篇文章主要介紹了idea自動(dòng)加載html、js而無需重啟進(jìn)程的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-08-08Java_異常類(錯(cuò)誤和異常,兩者的區(qū)別介紹)
下面小編就為大家?guī)硪黄狫ava_異常類(錯(cuò)誤和異常,兩者的區(qū)別介紹) 。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-09-09為Java應(yīng)用創(chuàng)建Docker鏡像的3種方式總結(jié)
Docker的使用可以將應(yīng)用程序做成鏡像,這樣可以將鏡像發(fā)布到私有或者公有倉(cāng)庫(kù)中,在其他主機(jī)上也可以pull鏡像,并且運(yùn)行容器,運(yùn)行程,下面這篇文章主要給大家總結(jié)介紹了關(guān)于為Java應(yīng)用創(chuàng)建Docker鏡像的3種方式,需要的朋友可以參考下2023-06-06簡(jiǎn)單了解springboot eureka交流機(jī)制
這篇文章主要介紹了簡(jiǎn)單了解springboot eureka交流機(jī)制,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04Spring Boot 使用 logback、logstash、ELK 記錄日志文件的方法
這篇文章主要介紹了Spring Boot 使用 logback、logstash、ELK 記錄日志文件的思路詳解,文中給大家提到了logback 取代 log4j的理由,需要的朋友可以參考下2017-12-12java小程序之控制臺(tái)字符動(dòng)畫的實(shí)現(xiàn)
這篇文章主要給大家介紹了java小程序之控制臺(tái)字符動(dòng)畫實(shí)現(xiàn)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04