Spring學習手札(二)面向切面程式設計AOP
AOP理解
Aspect Oriented Program面向切面程式設計,通過預編譯方式和執行期動態代理實現程式功能的統一維護的一種技術。
但是,這種說法有些片面,因為在軟體工程中,AOP的價值體現的並不是程式碼方面,更多的是為了專案的模組化,而不僅僅是為了減少重複程式碼。
AOP是一種程式設計思想,為的是讓模組本身變得更加內聚,讓開發者更多的關注到業務邏輯的開發。
在面向切面程式設計裡,把功能分為核心業務功能和周邊業務功能:
核心業務,比如登陸,日常增,刪資料
周邊功能,統計,日誌,事務管理。在Spring的面向切面程式設計AOP思想裡,即被定義為切面
在面向切面的思想裡,核心業務功能和切面功能單獨開發,然後把兩個整合在一起,就是AOP。
AOP實現原理
AOP底層將採用代理機制進行實現
介面+實現類:spring採用jdk的動態代理Proxy
實現類:spring採用cglib位元組碼增強
Spring預設使用JDK動態代理,在需要代理類而不是介面的時候,Spring會自動切換為使用cglib代理,不過現在的專案都是面向介面開發,所以JDK動態代理相對來說用的還是多一些。
Spring AOP引入了幾個概念
切面(Aspect),它是跨不同Java類層面的橫切性邏輯。可以通過XML檔案中配置的普通類,也可以在類程式碼中使用“@Aspect” 註解去宣告,在執行時,Spring會建立類似Advisor來代替它。其內部包括切入和通知。通俗的講,是要告訴Spring什麼時候,什麼地方,做什麼。
連線點(JoinPoint),程式執行過程中明確的點,一般是方法的呼叫。
通知(Advice),AOP在特定的切入點上知性的增強處理,告訴Spring什麼時候,做什麼。一般會講Advice模擬成一個攔截起,並且在JoinPoint上維護多個Advice,進行層攔截。比如Http鑑權的實現。
切入點(PointCut),就是帶有通知的連線點。要切入的物件,可以是類,或方法。在Spring中,所有的方法都可以認為是JoinPoint,都可以新增Advice,但是這並不是所有都是我們真正想要的,而PointCut提供了一組規則,來匹配JointPoint,給滿足規則的JointPoint新增Advice。其主要用來修飾JointPoint。
Advice的五種通知型別
前置通知(@Before):在目標方法被呼叫之前呼叫通知功能
後置通知(@After):在目標方法完成之後呼叫通知,此時不會關心方法的輸出
返回通知(@After-Returning):在目標方法成功執行之後呼叫通知
異常通知(@AfterThrowing):在目標方法丟擲異常後呼叫通知
環繞通知(@Around):通知包裹了被通知的方法,在被通知的方法呼叫之前和之後執行自定義的行為
做個小demo
1. 在src下新建bean包,新建Operator類
package bean; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; @Component @Aspect public class Operator { //@Pointcut("execution(* service.UserService.add()") //public void service() { //} @Pointcut("execution(* service.UserService.add())") public voidIService(){ } @Before("IService()") public void doBefore(JoinPoint joinPoint) { System.out.println("before Advice"); } @After("IService()") public void doAfter(JoinPoint joinPoint) { System.out.println("after Advice"); } @AfterReturning(pointcut = "IService()", returning = "returnVal") public void afterReturn(JoinPoint joinPoint, Object returnVal) { System.out.println(" after Advice return " + returnVal); } @AfterThrowing(pointcut = "IService()", throwing = "errors") public void afterThrowing(JoinPoint joinPoint, Throwable errors) { System.out.println("afterThrowing Advice ..." + errors); System.out.println(" afterThrowing.."); } @Around("IService()") public void doAround(ProceedingJoinPoint proceedingJoinPoint) { System.out.println(" around Advicebefore"); try { proceedingJoinPoint.proceed(); } catch (Throwable throwable) { throwable.printStackTrace(); } System.out.println(" around Advicebefore"); } }
2. src下新建service包,新建UserService類
package service; import org.springframework.stereotype.Service; @Service public class UserService { public void add() { System.out.println("UserService add."); } public boolean delete() { System.out.println("UserService delete .."); return true; } public void edit() { System.out.println("UserService edit "); } }
3.配置XML檔案,在src下新建applicationContext.xml
<?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"> <bean name="userService" class="service.UserService"></bean> <bean name="operator" class="bean.Operator"></bean> <aop:config> <aop:aspect id="operatorAspect" ref="operator"> <aop:pointcut id="servicePointCut" expression="execution(* service.UserService.add())"></aop:pointcut> <aop:before pointcut-ref="servicePointCut" method="doBefore"></aop:before> <aop:after pointcut-ref="servicePointCut" method="doAfter"></aop:after> <aop:around pointcut-ref="servicePointCut" method="doAround"></aop:around> <aop:after-returning pointcut-ref="servicePointCut" method="afterReturn" returning="returnVal"></aop:after-returning> <aop:after-throwing pointcut-ref="servicePointCut" method="afterThrowing" throwing="errors"></aop:after-throwing> </aop:aspect> </aop:config> </beans>
4.測試AOP,在src下新建test.java
import bean.User; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import service.UserService; public class test { @Test public void demo() { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); UserService userService = (UserService) context.getBean("userService", UserService.class); userService.add(); } }
5.執行結果