AOP(Aspect Orient Programming)是一種設計思想,是軟件設計領域中的面向切面編程,它是面向對象編程(OOP)的一種補充和完善。它以經過預編譯方式和運行期動態代理方式,實如今不修改源代碼的狀況下給程序動態統一添加額外功能的一種技術。
AOP與OOP字面意思相近,但其實二者徹底是面向不一樣領域的設計思想。實際項目中咱們一般將面向對象理解爲一個靜態過程(例如一個系統有多少個模塊,一個模塊有哪些對象,對象有哪些屬性),面向切面的運行期代理方式,理解爲一個動態過程,能夠在對象運行時動態織入一些擴展功能或控制對象執行。java
實際項目中一般會將系統分爲兩大部分,一部分是核心業務,一部分是非核業務。在編程實現時咱們首先要完成的是核心業務的實現,非核心業務通常是經過特定方式切入到系統中,這種特定方式通常就是藉助AOP進行實現。算法
AOP就是要基於OCP(開閉原則),在不改變原有系統核心業務代碼的基礎上動態添加一些擴展功能並能夠"控制"對象的執行。例如AOP應用於項目中的日誌處理,事務處理,權限處理,緩存處理等等。
spring
Spring AOP底層基於代理機制(動態方式)實現功能擴展:sql
說明:Spring boot2.x 中AOP如今默認使用的CGLIB代理,假如須要使用JDK動態代理能夠在配置文件(applicatiion.properties)中進行以下配置:數據庫
#cglib aop proxy #spring.aop.proxy-target-class=true #jdk aop proxy spring.aop.proxy-target-class=false
第一步建立maven項目或在已有項目基礎上添加AOP啓動依賴:編程
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
第二步定義業務層接口api
package com.cy.pj.common.service; public interface MailService { boolean sendMail(String msg); }
第三步定義業務層實現類緩存
@Service public class MailServiceImpl implements MailService{ @Override public boolean sendMail(String msg) {//ocp(開閉原則-->對擴展開放,對修改關閉) long t1=System.currentTimeMillis(); System.out.println("send->"+msg); long t2=System.currentTimeMillis(); System.out.println("send time:"+(t2-t1)); return true; } }
咱們本身計算了執行時間,可是違反了ocp原則,因此須要無侵入式擴展這個記錄執行時間的功能。咱們在這個類中添加內部類來實現兩種方式的擴展。架構
package com.cy.pj.common.service; import org.springframework.stereotype.Service; @Service public class MailServiceImpl implements MailService{ @Override public boolean sendMail(String msg) {//ocp(開閉原則-->對擴展開放,對修改關閉) //long t1=System.currentTimeMillis(); System.out.println("send->"+msg); //long t2=System.currentTimeMillis(); //System.out.println("send time:"+(t2-t1)); return true; } } //下面的兩種設計瞭解?(基於原生方式實現功能擴展) //本身動手寫子類重寫父類方法進行功能擴展 class TimeMailServiceImpl extends MailServiceImpl{//這種寫法的原型就是CGLIB代理機制的方式(繼承) @Override public boolean sendMail(String msg) { long t1=System.currentTimeMillis(); boolean flag=super.sendMail(msg); long t2=System.currentTimeMillis(); System.out.println("send time:"+(t2-t1)); return flag; } } //本身寫兄弟類對目標對象(兄弟類)進行功能擴展,這種方式又叫組合 class TimeMailServiceImpl2 implements MailService{//這種寫法的原型就是JDK代理機制的方式(實現) private MailService mailService; public TimeMailServiceImpl2(MailService mailService){ this.mailService=mailService; } @Override public boolean sendMail(String msg) { long t1=System.currentTimeMillis(); boolean flag=mailService.sendMail(msg); long t2=System.currentTimeMillis(); System.out.println("send time:"+(t2-t1)); return flag; } }
下來經過aop方式完成業務併發
package com.cy.pj.common.service.aspect; 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; @Aspect//告訴spring我是一個切面(封裝了擴展邏輯對象),這樣的對象中要包含兩部份內容(1.切入點,2.擴展邏輯-advice) @Component//表示在spring中作一個註冊 public class SysLogAspect { //定義切入點 //bean表達式爲spring中的一種粗粒度切入點表達式(不能精確到具體方法) //這裏的mailServiceImpl名字爲spring容器中一個bean對象的名字 @Pointcut("bean(mailServiceImpl)")//多個bean的定義形式(bean(*ServiceImpl)) public void doLogPointCut(){}//這個方法僅僅是承載切入點註解的一個載體,方法體內不須要寫任何內容 /*按照Aspect規範定義一個@Around環繞通知*/ //@Around("bean(mailServiceImpl)")//直接在advice註解內部定義切入點表達式 //對於@Around註解描述的方法器規範要求 //1)返回值類型爲Object(用於封裝目標方法的執行結果) //2)參數類型爲ProceedingJoinPoint(用於封裝執行的目標方法信息) //3)拋出的異常Throwable(用於封裝執行目標方法時拋出的異常) //4)在@Around註解描述的方法內部,能夠手動調用目標方法 @Around("doLogPointCut()")//也能夠在advice註解內部經過方法引用引入切入點表達式 public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable{ long t1=System.currentTimeMillis(); Object result = joinPoint.proceed();//表示調用目標方法 long t2=System.currentTimeMillis(); System.out.println("send time:"+(t2-t1)); return result; } }
編寫測試類來測試實現
package com.cy.pj.common.service; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest public class MailServiceTests { @Autowired private MailService mailService; @Test//面向切面的測試類 void testSendMail03(){ mailService.sendMail("hello mailService"); } @Test//本身動手寫的子類測試 void testSendMail01(){ //new TimeMailServiceImpl().sendMail("hello aop"); } @Test//本身動手寫的兄弟類測試 void testSendMail02(){ //new TimeMailServiceImpl2(new MailServiceImpl()).sendMail("hello CGB2007"); } }
整個aop面向切面編程的過程圖示
debug追蹤cglib代理的注入對象
debug追蹤jdk代理的注入對象
假如目標對象有實現接口,則能夠基於JDK爲目標對象建立代理對象,而後爲目標對象進行功能擴展
說明:假如目標對象類型沒有實現接口,則不容許使用JDK代理。
假如目標對象沒有實現接口(固然實現了接口也是能夠的),能夠基於CGLIB代理方式爲目標對象織入功能擴展
說明:目標對象實現了接口也能夠基於CGLIB爲目標對象建立代理對象。可是目標對象類型假如使用了final修飾,則不可使用CGBLIB。
在基於Spring AOP編程的過程當中,基於AspectJ框架標準,spring中定義了五種類型的通知(通知-Advice描述的是一種擴展業務),它們分別是:
package com.cy.pj.common.aspect; @Component @Aspect public class SysTimeAspect { @Pointcut("bean(sysUserServiceImpl)") public void doTime(){} @Before("doTime()") public void doBefore(){ System.out.println("time doBefore()"); } @After("doTime()") public void doAfter(){ System.out.println("time doAfter()"); } /**核心業務正常結束時執行* 說明:假若有after,先執行after,再執行returning*/ @AfterReturning("doTime()") public void doAfterReturning(){ System.out.println("time doAfterReturning"); } /**核心業務出現異常時執行說明:假若有after,先執行after,再執行Throwing*/ @AfterThrowing("doTime()") public void doAfterThrowing(){ System.out.println("time doAfterThrowing"); } @Around("doTime()") public Object doAround(ProceedingJoinPoint jp) throws Throwable{ System.out.println("doAround.before"); try{ Object obj=jp.proceed(); System.out.println("doAround.after"); return obj; }catch(Throwable e){ System.out.println(e.getMessage()); throw e; } } }
bean表達式通常應用於類級別,實現粗粒度的切入點定義,案例分析:
說明:bean表達式內部的對象是由spring容器管理的一個bean對象,表達式內部的名字應該是spring容器中某個bean的name。
缺陷:不能精確到具體方法,也不能針對於具體模塊包中的方法作切入點設計
within表達式應用於類級別,實現粗粒度的切入點表達式定義,案例分析:
within表達式應用場景分析:
1)對全部業務bean都要進行功能加強,可是bean名字又沒有規則。
2)按業務模塊(不一樣包下的業務)對bean對象進行業務功能加強。
execution表達式應用於方法級別,實現細粒度的切入點表達式定義,案例分析:
語法:execution(返回值類型 包名.類名.方法名(參數列表))。
@annotaion表達式應用於方法級別,實現細粒度的切入點表達式定義,案例分析
切面的優先級須要藉助@Order註解進行描述,數字越小優先級越高,默認優先級比較低。例如:
定義日誌切面並指定優先級。
@Order(1) @Aspect @Component public class SysLogAspect { … } 定義緩存切面並指定優先級: @Order(2) @Aspect @Component public class SysCacheAspect { … }
說明:當多個切面做用於同一個目標對象方法時,這些切面會構建成一個切面鏈,相似過濾器鏈、攔截器鏈
Spring 基於AspectJ框架實現AOP設計的關鍵對象概覽
事務(Transaction)是一個業務,是一個不可分割的邏輯工做單元,基於事務能夠更好的保證業務的正確性。
說明:目前市場上在事務一致性方面,一般會作必定的優化,比方說只要最終一致就能夠了,這樣的事務咱們一般會稱之爲柔性事務(只要最終一致就能夠了).
Spring框架中提供了一種聲明式事務的處理方式,此方式基於AOP代理,能夠將具體業務邏輯與事務處理進行解耦。也就是讓咱們的業務代碼邏輯不受污染或少許污染,就能夠實現事務控制。
在SpringBoot項目中,其內部提供了事務的自動配置,當咱們在項目中添加了指定依賴spring-boot-starter-jdbc時,框架會自動爲咱們的項目注入事務管理器對象,最經常使用的爲DataSourceTransactionManager對象。
實際項目中最經常使用的註解方式的事務管理,以註解@Transactional配置方式爲例,進行實踐分析。
基於@Transactional 註解進行聲明式事務管理的實現步驟分爲兩步:
1) 啓用聲明式事務管理,在項目啓動類上添加@EnableTransactionManagement,新版本中也可不添加(例如新版Spring Boot項目)。
2) 將@Transactional註解添加到合適的業務類或方法上,並設置合適的屬性信息。
@Transactional(timeout = 30, readOnly = false, isolation = Isolation.READ_COMMITTED, rollbackFor = Throwable.class, propagation = Propagation.REQUIRED) @Service public class implements SysUserService { @Transactional(readOnly = true) @Override public PageObject<SysUserDeptVo> findPageObjects( String username, Integer pageCurrent) { … } }
其中,代碼中的@Transactional註解用於描述類或方法,告訴spring框架咱們要在此類的方法執行時進行事務控制,其具體說明以下:。
▪ 當@Transactional註解應用在類上時表示類中全部方法啓動事務管理,而且通常用於事務共性的定義。
▪ 當@Transactional描述方法時表示此方法要進行事務管理,假如類和方法上都有@Transactional註解,則方法上的事務特性優先級比較高。
@Transactional經常使用屬性應用說明:
▪ timeout:事務的超時時間,默認值爲-1,表示沒有超時顯示。若是配置了具體時間,則超過該時間限制但事務尚未完成,則自動回滾事務。這個時間的記錄方式是在事務開啓之後到sql語句執行以前。
▪ read-only:指定事務是否爲只讀事務,默認值爲 false;爲了忽略那些不須要事務的方法,好比讀取數據,能夠設置read-only爲true。對添加,修改,刪除業務read-only的值應該爲false。
▪ rollback-for:用於指定可以觸發事務回滾的異常類型,若是有多個異常類型須要指定,各種型之間能夠經過逗號分隔。
▪ no-rollback- for: 拋出no-rollback-for 指定的異常類型,不回滾事務。
▪ isolation:事務的隔離級別,默認值採用 DEFAULT。當多個事務併發執行時,可能會出現髒讀,不可重複讀,幻讀等現象時,但假如不但願出現這些現象可考慮修改事務的隔離級別(但隔離級別越高併發就會越小,性能就會越差)
Spring中事務控制過程分析
Spring事務管理是基於接口代理(JDK)或動態字節碼(CGLIB)技術,而後經過AOP實施事務加強的。當咱們執行添加了事務特性的目標方式時,系統會經過目標對象的代理對象調用DataSourceTransactionManager對象,在事務開始的時,執行doBegin方法,事務結束時執行doCommit或doRollback方法。
Spring 中事務傳播特性
事務傳播(Propagation)特性指"不一樣業務(service)對象"中的事務方法之間相互調用時,事務的傳播方式
其中,經常使用事務傳播方式:
@Transactional(propagation=Propagation.REQUIRED)。
若是沒有事務建立新事務, 若是當前有事務參與當前事務, Spring 默認的事務傳播行爲是PROPAGATION_REQUIRED,它適合於絕大多數的狀況。假設 ServiveX#methodX() 都工做在事務環境下(即都被 Spring 事務加強了),假設程序中存在以下的調用鏈:
Service1#method1()->Service2#method2()->Service3#method3(),那麼這 3 個服務類的 3 個方法經過 Spring 的事務傳播機制都工做在同一個事務中。
@Transactional(propagation = Propagation.REQUIRED) @Override public List<Node> findZtreeMenuNodes() { return sysMenuDao.findZtreeMenuNodes(); }
當有一個業務對象調用如上方法時,此方法始終工做在一個已經存在的事務方法,或者是由調用者建立的一個事務方法中。
@Transactional(propagation=Propagation.REQUIRES_NEW)。
必須是新事務, 若是有當前事務, 掛起當前事務而且開啓新事務,
@Transactional(propagation = Propagation.REQUIRES_NEW) @Override public void saveObject(SysLog entity) { sysLogDao.insertObject(entity); }
當有一個業務對象調用如上業務方法時,此方法會始終運行在一個新的事務中。
在開發系統的過程當中,一般會考慮到系統的性能問題,提高系統性能的一個重要思想就是「串行」改「並行」。提及「並行」天然離不開「異步」,今天咱們就來聊聊如何使用Spring的@Async的異步註解。
在基於註解方式的配置中,藉助@EnableAsync註解進行異步啓動聲明,Spring Boot版的項目中,將@EnableAsync註解應用到啓動類上
@EnableAsync //spring容器啓動時會建立線程池 @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
在須要異步執行的業務方法上,使用@Async方法進行異步聲明。
@Async @Transactional(propagation = Propagation.REQUIRES_NEW) @Override public void saveObject(SysLog entity) { System.out.println("SysLogServiceImpl.save:"+ Thread.currentThread().getName()); sysLogDao.insertObject(entity); //try{Thread.sleep(5000);}catch(Exception e) {} }
假如須要獲取業務層異步方法的執行結果,可參考以下代碼設計進行實現:
@Transactional(propagation = Propagation.REQUIRES_NEW) @Async @Override public Future<Integer> saveObject(SysLog entity) { System.out.println("SysLogServiceImpl.save:"+ Thread.currentThread().getName()); int rows=sysLogDao.insertObject(entity); //try{Thread.sleep(5000);}catch(Exception e) {} return new AsyncResult<Integer>(rows); }
其中,AsyncResult對象能夠對異步方法的執行結果進行封裝,假如外界須要異步方法結果時,能夠經過Future對象的get方法獲取結果。
當咱們須要本身對spring框架提供的線程池進行一些簡易配置
spring: task: execution: pool: queue-capacity: 128 core-size: 5 max-size: 128 keep-alive: 60000 thread-name-prefix: db-service-task-
對於spring框架中線程池配置參數的涵義,能夠參考ThreadPoolExecutor對象中的解釋。
說明:對於@Async註解默認會基於ThreadPoolTaskExecutor對象獲取工做線程,而後調用由@Async描述的方法,讓方法運行於一個工做線程,以實現異步操做。可是假如系統中的默認拒絕處理策略,任務執行過程的異常處理不能知足咱們自身業務需求的話,我能夠對異步線程池進行自定義.(SpringBoot中默認的異步配置能夠參考自動配置對象TaskExecutionAutoConfiguration).
爲了讓Spring中的異步池更好的服務於咱們的業務,同時也儘可能避免OOM,能夠自定義線程池優化設計以下:
package com.cy.pj.common.config @Slf4j @Setter @Configuration @ConfigurationProperties("async-thread-pool") public class SpringAsyncConfig implements AsyncConfigurer{ /**核心線程數*/ private int corePoolSize=20; /**最大線程數*/ private int maximumPoolSize=1000; /**線程空閒時間*/ private int keepAliveTime=30; /**阻塞隊列容量*/ private int queueCapacity=200; /**構建線程工廠*/ private ThreadFactory threadFactory=new ThreadFactory() { //CAS算法 private AtomicInteger at=new AtomicInteger(1000); @Override public Thread newThread(Runnable r) { return new Thread(r, "db-async-thread-"+at.getAndIncrement()); } }; @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(corePoolSize); executor.setMaxPoolSize(maximumPoolSize); executor.setKeepAliveSeconds(keepAliveTime); executor.setQueueCapacity(queueCapacity); executor.setRejectedExecutionHandler((Runnable r, ThreadPoolExecutor exe) -> { log.warn("當前任務線程池隊列已滿."); }); executor.initialize(); return executor; } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return new AsyncUncaughtExceptionHandler() { @Override public void handleUncaughtException(Throwable ex , Method method , Object... params) { log.error("線程池執行任務發生未知異常.", ex); } }; }}
其中:@ConfigurationProperties("async-thread-pool")的含義是讀取application.yml配置文件中以"async-thread-pool"名爲前綴的配置信息,並經過所描述類的set方法賦值給對應的屬性,在application.yml中鏈接器池的關鍵配置以下:
async-thread-pool: corePoolSize: 20 maxPoolSize: 1000 keepAliveSeconds: 30 queueCapacity: 1000
後續在業務類中,假如咱們使用@Async註解描述業務方法,默認會使用ThreadPoolTaskExecutor池對象中的線程執行異步任務。
在業務方法中咱們可能調用數據層方法獲取數據庫中數據,假如訪問數據的頻率比較高,爲了提升的查詢效率,下降數據庫的訪問壓力,能夠在業務層對數據進行緩存.
在項目(SpringBoot項目)的啓動類上添加@EnableCaching註解,以啓動緩存配置。
package com.cy; /** * 異步的自動配置生效). * @EnableCaching 註解表示啓動緩存配置 */ @EnableCaching @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
在須要進行緩存的業務方法上經過@Cacheable註解對方法進行相關描述.表示方法的返回值要存儲到Cache中,假如在更新操做時須要將cache中的數據移除,能夠在更新方法上使用@CacheEvict註解對方法進行描述。
第一步:在相關模塊查詢相關業務方法中,使用緩存
@Cacheable(value = "menuCache") @Transactional(readOnly = true) public List<Map<String,Object>> findObjects() { .... }
其中,value屬性的值表示要使用的緩存對象,名字本身指定,其中底層爲一個map對象,當向cache中添加數據時,key默認爲方法實際參數的組合。
第二步:在相關模塊更新時,清除指定緩存數據
allEntries表示清除全部。
@CacheEvict(value="menuCache",allEntries=true) @Override public int saveObject(SysDept entity) {...}
spring中的緩存應用原理
第一步定義業務接口
package com.cy.pj.module.service; import java.util.List; public interface ModuleService { List<String> findPermissions(); }
第二步定義業務實現類
package com.cy.pj.module.service; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; @Service public class ModuleServiceImpl implements ModuleService{ @Override public List<String> findPermissions() { System.out.println("select permissions from database"); List<String> list=new ArrayList<>(); list.add("sys:log:delete"); list.add("sys:log:select");//假設這些數據來自數據庫 return list; } }
第三步定義切面
package com.cy.pj.commom.aspect; 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; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @Component @Aspect public class CacheAspect {//系統底層會將這個切面中的內容轉換爲Advisor對象 //假設這個map就是咱們的一個小cache,咱們從數據庫取出的數據能夠存儲到此cache中 private Map<String,Object> cache=new ConcurrentHashMap<>(); @Pointcut("bean(moduleServiceImpl)") public void doCache(){} @Around("doCache()") public Object around(ProceedingJoinPoint joinPoint) throws Throwable{ //1.從cache中取數據 Object obj = cache.get("userPer");//假設key爲userPer if(obj!=null) return obj; //2.cache中沒有則查數據 obj= joinPoint.proceed(); //3.將數據存儲到cache cache.put("userPer", obj); return obj; } }
第四步編寫測試類測試
package com.cy.pj.module.service; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.util.List; @SpringBootTest public class ModuleServiceTests { @Autowired private ModuleService moduleService; @Test void testFindPermissions(){ List<String> permissions = moduleService.findPermissions(); permissions = moduleService.findPermissions(); permissions = moduleService.findPermissions(); } }
Spring 整合AspectJ框架實現AOP只是Spring框架中AOP的一種實現方式,此方式相對比較簡單,實現方便。但此方式底層仍是要轉換爲Spring原生AOP的實現,Spring AOP原生方式實現的核心有兩大部分構成,分別是:
▪ 代理(JDK,CGLIB)。
▪ org.aopalliance包下的攔截體系。
以Spring中一種原生AOP架構的基本實現爲例進行原理分析和說明,其簡易架構
其中DefaultAdvisorAutoProxyCreator這個類功能更爲強大,這個類的奇妙之處是他實現BeanPostProcessor接口,當ApplicationContext讀取全部的Bean配置信息後,這個類將掃描上下文,尋找全部的Advisor對象(一個Advisor由切入點和通知組成),將這些Advisor應用到全部符合切入點的Bean中。
package com.cy.pj.common.service; public interface MailService { boolean sendMsg(String message); }
package com.cy.pj.common.service; import org.springframework.stereotype.Service; @Service public class MailServiceImpl implements MailService{ @Override public boolean sendMsg(String message) { System.out.println("send->"+message); return true; } }
其中,MethodInterceptor對象繼承Advice對象,基於此對象方法能夠對目標方法進行攔截。
package com.cy.pj.common.advisor; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; /**封裝了擴展業務邏輯的對象,這樣的對象在原生的aop中須要在advisor中註冊*/ public class LogAdvice implements MethodInterceptor {//Advice /**此方法能夠在目標業務方法執行以前和以後添加擴展邏輯*/ @Override public Object invoke(MethodInvocation methodInvocation) throws Throwable { System.out.println("start:"+System.nanoTime()); Object result = methodInvocation.proceed();//執行目標方法 System.out.println("end:"+System.nanoTime()); return result; } }
其中,StaticMethodMatcherPointcutAdvisor類爲Spring框架中定義的一種Advisor,咱們本身寫的Advisor能夠直接繼承此類進行資源整合。
package com.cy.pj.common.advisor; import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor; import org.springframework.stereotype.Component; import java.lang.reflect.Method; /** * 此Advisor中定義了一種規範 * 1)定義了哪些方法爲切入點方法 * 2)定義了在切入點方法執行時要植入的通知(擴展邏輯) */ @Component public class LogAdvisor extends StaticMethodMatcherPointcutAdvisor { //定通知 advice public LogAdvisor(){ setAdvice(new LogAdvice()); } //定切點 pointcut /** * matches方法中能夠獲取咱們要執行的目標方法,而且咱們能夠在此判斷這個目標方法是否爲咱們的一個切入點方法 * 1)返回值爲true表示目標方法爲切入點方法(在此方法執行時能夠植入擴展邏輯) * 2)返回值爲false表示目標方法爲非切入點方法 */ @Override public boolean matches(Method method, Class<?> aClass) { try { Method targetMethod = aClass.getMethod(method.getName(), method.getParameterTypes()); return targetMethod.getName().equals("sendMsg"); }catch (Exception e){ return false; } } }
在項目啓動類中,添加DefaultAdvisorAutoProxyCreator對象初始化方法,基於此對象在容器啓動時掃描全部Advisor對象,而後基於切入點描述的目標方法爲目標對象建立代理對象
package com.cy; import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; @SpringBootApplication public class Application { @Bean //@Bean註解描述方法時,這個方法的返回值交給spring管理 public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){ return new DefaultAdvisorAutoProxyCreator(); } public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
package com.cy.pj.common.service; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest public class MailServiceTests { @Autowired private MailService mailService; @Test void testSendMsg(){ System.out.println(mailService.sendMsg("hello spring aop")); } }