《劍風傳奇》全新主題霸王之卵墜飾 蘊含神祕之力
阿新 • • 發佈:2022-01-09
AOP簡介
AOP(Aspect Oriented Progrmming)面向切面程式設計,通過預編譯方式和執行期間動態代理實現功能的統一維護的一種技術。
Aop的作用
- 在程式執行期間,不修改程式碼的同時為程式增強功能。
- 將必不可少的公共功能·做成切面,隨程式執行切入到程式碼中執行。
- 編寫業務時只關注於核心功能,減輕編碼負擔,更專注業務。
AOP的術語
- 連線點(join point):程式執行的某個特定位置,例如一個擁有兩個方法的類,這兩個方法都是連線點。
- 切入點(Pointcut);每一個程式類都擁有多個連線點,AOP通過切入點定位特定的連線點。連線點相當於資料庫中的記錄,切入點就是查詢條件,一個切點可以匹配多個連線點。切點定義了在哪裡做。
- 通知(Advice):通知是織入到目標連線點上的一段程式碼。在Spring中,通知除了用於描述一段程式碼外,還擁有另一個和連線點相關的資訊,即執行點方位。結合執行點方位資訊和切點資訊,我們就可以找到特定的連線點。通知定義了需要做什麼,在某個連線點的什麼時候做。
- Spring切面可以應用五種型別的通知
- 前置通知(Before):在目標方法被呼叫之前呼叫通知功能
- 前置通知(After):在目標方法被呼叫之前呼叫通知功能
- 返回通知(After-returning):在方法成功執行之後呼叫通知
- 異常通知(After-throwing):在目標方法丟擲異常後呼叫通知
- 環繞通知(Around):通知包裹了被通知的方法,在被通知的方法呼叫之前和呼叫之後執行自定義的行為。環繞通知可以修改入參值
- 切面(Aspect):切面由切點和通知組成,它既包括了橫切邏輯的定義,也包括連線點的定義。Spring AOP就是負責實施切面的框架,它將切面所定義的橫切邏輯織入到切面所指定的連線點中。
- 切面是切點的通知的結合,切面知道所有它需要做的事:何時做,何處做,做什麼
- 目標物件(Target object):即需要增強的物件,通知邏輯的織入目標類。如果沒有AOP,目標業務類需要自己實現所有邏輯,而在AOP的幫助下,目標業務類只實現那些非橫切邏輯的程式邏輯,而效能監視和事務管理等這些橫切邏輯則可以使用AOP動態織入到特定的連線點上
- 織入(Weaving):織入是將通知新增對目標類具體連線點上的過程。AOP像一臺織布機,將目標類、通知或引介通過AOP這臺織布機天衣無縫地編織到一起。根據不同的實現技術,AOP有三種織入的方式:
- 編譯期織入,這要求使用特殊的Java編譯器
- 類裝載期織入,這要求使用特殊的類裝載器
- 動態代理織入,在執行期為目標類新增通知生成子類的方式
- 織入是把切面應用到目標物件來建立新的代理物件的過程,Spring採用動態代理織入,而AspectJ採用編譯期織入和類裝載期織入
- 代理(Proxy):一個類被AOP織入通知後,就產出了一個結果類,它是融合了原類和通知邏輯的代理類。根據不同的代理方式,代理類既可能是和原類具有相同介面的類,也可能就是原類的子類,所以我們可以採用呼叫原類相同的方式呼叫代理類
使用註解實現AOP
常用註解:
@Aspect 切面,配置到切面類上
@PointCut("表示式") 配置切入點,加在方法上
@Before 配置前置通知方法
@After 配置後置通知方法
@Around 配置環繞通知方法
@AfterReturning 配置後置返回值通知方法
@AfterThrowing 配置後置丟擲異常通知方法
例子:用AOP完成對service層所有方法的日誌跟蹤,日誌能輸出當前方法名,傳入的引數值,執行時間(使用環繞)
導包:
- spring-context
- spring-aop
- spring-test
- aspectjrt
- aspectjweaver
- junit
配置類:
SpringConfig.java
//配置類註解 @Configurable //包掃描註解 @ComponentScan(basePackages = "cn.blb.aop") //開機aop註解 @EnableAspectJAutoProxy public class SpringConfig { }
使用者服務介面:
UserService.java
//使用者服務介面 public interface UserService { //方法1 public String updateMoney(String name,double money); //方法2 public String updateMoney2(String name); }
實現使用者服務介面:
UserServiceImpl.java
//spring服務層註解 @Service //實現使用者服務介面 public class UserServiceImpl implements UserService { @Override public String updateMoney(String name, double money) { System.out.println("我向"+name+"轉賬"+money); return name; } @Override public String updateMoney2(String name) { System.out.println("我向"+name+"轉賬"); return name; } }
增加的功能:
UserLogService.java
//配置切面 @Aspect @Component public class UserLogService { //配置切點 @Pointcut("execution(* cn.blb.aop..*.*(..))") public void pointcut() { } //使用通知環繞 @Around("pointcut()") public void getLog(ProceedingJoinPoint proceedingJoinPoint) { //獲取方法名 String name = proceedingJoinPoint.getSignature().getName(); System.out.println("方法名:"+name); //獲取傳入的引數 Object[] args = proceedingJoinPoint.getArgs(); System.out.println("傳入的引數有:"); for (Object a : args) { System.out.println(a); } //獲取當前時間 Date data = new Date(); //設定時間格式 SimpleDateFormat form = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String time = form.format(data); System.out.println("執行時間" + time); try { //呼叫增強的方法 proceedingJoinPoint.proceed(); } catch (Throwable throwable) { throwable.printStackTrace(); } } }
值得一提的是:環繞通知可以修改入參值
呼叫被增強的方法時,呼叫另一個processd(new Object[ ]{引數值})
測試類:
Test.java
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = SpringConfig.class) public class testUserService { //Spring的自動裝配註解 @Autowired private UserService userService; @Test public void testService(){ userService.updateMoney("張山",2000D); userService.updateMoney2("李四"); } }
執行結果: