Spring 框架的一個關鍵組件是面向方面的編程(AOP)框架。面向方面的編程需要把程序邏輯分解成不同的部分稱為所謂的關注點。跨一個應用程序的多個點的功能被稱為橫切關注點,這些橫切關注點在概念上獨立於應用程序的業務邏輯。有各種各樣的常見的很好的方面的例子,如日志記錄、審計、聲明式事務、安全性和緩存等。
AOP實現原理:Spring AOP是基於動態代理機制實現的,通過動態代理機制生成目標對象的代理對象,當外部調用目標對象的相關方法時,Spring注入的其實是代理對象Proxy,通過調用代理對象的方法執行AOP增強處理,然后回調目標對象的方法。
AOP中的相關概念
Aspect(切面): Aspect 聲明類似於 Java 中的類聲明,在 Aspect 中會包含着一些 Pointcut 以及相應的 Advice。
Joint point(連接點):表示在程序中明確定義的點,典型的包括方法調用,對類成員的訪問以及異常處理程序塊的執行等等,它自身還可以嵌套其它 joint point。
Pointcut(切點):表示所有連接點中的實際要執行的 joint point,這些 joint point 或是通過邏輯關系組合起來,或是通過通配、正則表達式等方式集中起來,它定義了相應的 Advice 將要發生的地方。
Advice(增強):Advice 定義了在 Pointcut 里面定義的程序點具體要做的操作,它通過 before、after 和 around 來區別是在每個 joint point 之前、之后還是代替執行的代碼。
Target(目標對象):織入 Advice 的目標對象.。
Weaving(織入):將 Aspect 和其他對象連接起來, 並創建 Adviced object 的過程。
AOP有兩種實現方式:
1.基於xml配置實現 2通過注解方式實現
方式一:基於xml配置實現
引入依賴的jar包:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>4.3.12.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.12.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.3.12.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>4.3.12.RELEASE</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.13</version> </dependency>
編寫目標類:
public class Count { public void add() { System.out.println("add ..."); } }
編寫增強類和增強方法:
import org.aspectj.lang.ProceedingJoinPoint; public class CountProxy { public void beforeCall(){ System.out.println("前置增強。。。"); } public void afterCall(){ System.out.println("后置增強。。。"); } public void aroundCall(ProceedingJoinPoint point) throws Throwable { System.out.println("環形增強,執行方法前。。。"); long start = System.currentTimeMillis(); point.proceed(); long end=System.currentTimeMillis(); System.out.println("環形增強,執行方法后。。。"); System.out.println("方法耗時:"+(end-start)+"ms"); } }
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 http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="count" class="com.aopxml.Count"/> <bean id="countProxy" class="com.aopxml.CountProxy"/> <!-- 配置AOP的操作 --> <aop:config> <!-- 配置切入點,對類Count里面的所有方法都增強 --> <aop:pointcut expression="execution(* com.aopxml.Count.*(..))" id="pointcut1"></aop:pointcut> <!-- 配置切面 aop:aspect標簽里面使用屬性ref,ref屬性值寫增強類的bean的id值 --> <aop:aspect ref="countProxy"> <!-- 增強類型 method屬性:增強類的方法名稱 pointcut-ref屬性:切入點的id值 --> <!-- 前置通知 --> <aop:before method="beforeCall" pointcut-ref="pointcut1"></aop:before> <!-- 后置通知 --> <aop:after method="afterCall" pointcut-ref="pointcut1"></aop:after> <!-- 環形通知 --> <aop:around method="aroundCall" pointcut-ref="pointcut1"></aop:around> </aop:aspect> </aop:config> </beans>
編寫測試類:
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test { public static void main(String[] args) { ApplicationContext context=new ClassPathXmlApplicationContext("bean3.xml"); Count count= (Count) context.getBean("count"); count.add(); } }
運行結果:
前置增強。。。
環形增強,執行方法前。。。
add ...
環形增強,執行方法后。。。
方法耗時:45ms
后置增強。。。
方式二:基於 @AspectJ注解的實現方式
導入依賴jar包:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>4.3.12.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.12.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.3.12.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>4.3.12.RELEASE</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.13</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.8.13</version> </dependency>
配置xml文件開啟@Aspectj支持,需要引入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: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"> <!-- 啟動@AspectJ支持,自動搜索切面類 --> <aop:aspectj-autoproxy/> <!-- component-scan只能識別@Compent、@Controller、@Service、@Repository注解--> <context:component-scan base-package="com.aopanno"> <context:include-filter type="annotation" expression="org.aspectj.lang.annotation.Aspect" /> </context:component-scan> </beans>
如果不打算使用XML Schema的配置方式,則應該在Spring配置文件中增加如下片段來啟用@AspectJ支持(即上面的<aop:aspectj-autoproxy />和下面創建Bean的方式選擇一種即可啟用@AspectJ支持):
<bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator" />
編寫目標類:
public @interface MyTransaction { }
import org.springframework.stereotype.Component; @Component public class Count { public void add() { System.out.println("add ..."); } @MyTransaction//自定義注解 public void delete(){ System.out.println("delete ..."); } }
編寫增強類:
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Aspect @Component public class CountProxy { @Before(value = "execution(* com.aopanno.Count.add(..))") public void beforeCall(){ System.out.println("注解方式add方法前置增強。。。"); } @After("execution(* com.aopanno.Count.add(..))") public void afterCall(){ System.out.println("注解方式add方法后置增強。。。"); } @Around("execution(* com.aopanno.Count.add(..))") public void aroundCall(ProceedingJoinPoint point) throws Throwable { System.out.println("注解方式環形增強,執行add方法前。。。"); long start = System.currentTimeMillis(); point.proceed(); long end=System.currentTimeMillis(); System.out.println("注解方式環形增強,執行add方法后。。。"); System.out.println("方法耗時:"+(end-start)+"ms"); } @Around("@annotation(MyTransaction)")//通過自定義注解標簽匹配 public void myAnnoAdvice(ProceedingJoinPoint point) throws Throwable { System.out.println("自定義注解方式環形增強,執行delete方法前。。。"); point.proceed(); System.out.println("自定義注解方式環形增強,執行delete方法后。。。"); } }
編寫測試類:
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test { public static void main(String[] args) { ApplicationContext context=new ClassPathXmlApplicationContext("bean4.xml"); //根據類型獲取bean對象 Count count= context.getBean(Count.class); count.add();
count.delete(); } }
運行結果:
注解方式環形增強,執行add方法前。。。
注解方式add方法前置增強。。。
add ...
注解方式環形增強,執行add方法后。。。
方法耗時:48ms
注解方式add方法后置增強。。。
自定義注解方式環形增強,執行delete方法前。。。
delete ...
自定義注解方式環形增強,執行delete方法后。。。