AOP 即 Aspect Oriented Program 面向切面編程
首先,在面向切面編程的思想裏面,把功能分爲核心業務功能,和周邊功能。
所謂的核心業務,好比登錄,增長數據,刪除數據都叫核心業務
所謂的周邊功能,好比性能統計,日誌,事務管理等等 java
周邊功能在Spring的面向切面編程AOP思想裏,即被定義爲切面 spring
在面向切面編程AOP的思想裏面,核心業務功能和切面功能分別獨立進行開發
而後把切面和核心業務功能 "編織" 在一塊兒,這就叫AOPexpress
注意:Spring會根據是否實現了接口自動切換JDK動態代理和CGLib動態代理
1.若是是使用Jdk動態代理實現Spring AOP,Spring容器的getBean方法得到的對象是不能轉型成該Bean定義的Class類型。
假設有Service接口和實現該接口的ServiceImpl類,使用Jdk動態代理實現Spring AOP,getBean得到的對象是Jdk動態代理生成的代理類的對象,這個代理類只是實現了Service接口,而沒有繼承ServiceImpl。 使用CGLib動態代理不會有這個問題,由於CGLib動態代理生成的代理類是繼承咱們的目標類的,而不是實現目標接口。編程
2.若是使用CGLib動態代理實現Spring AOP,經過Spring容器的getBean方法得到的對象不能直接引用目標類的公有屬性,讀取或者修改公有屬性。
假設Service類沒有實現任何接口,使用Spring容器的getBean方法時轉型成Service類的對象service,但咱們不能調用service.type來得到或者修改type屬性。
這是由於使用CGLib動態代理實現的Spring AOP,調用service.type是引用CGLib代理類對象的屬性,而不是目標對象的type屬性。app
總之:若是實現了接口,getBean要強轉成接口,若是沒有實現接口,則能夠轉成實現類。框架
首先了解一下幾種註解的做用:
@Aspect 將某個類聲明成一個切面函數
@Pointcut("切入點表達式") 聲明一個切點,咱們可利用方法簽名來編寫切入點表達式。最典型的切入點表達式是根據方法的簽名來匹配各類方法:性能
@Before("切入點表達式或已經聲明的切入點")前置通知方法。 前置通知方法在目標方法開始以前執行。spa
@AfterReturning("")返回前執行代理
@AfterThrowing("") 拋出異常前執行
@After("") 後置通知。方法執行完後必定會執行。相似finaly
@Around("") 環繞通知。被攔截(被切入)方法的調用由環繞通知決定。切面方法須要傳入一個ProceedingJointPoint對象,該對象用於啓用被攔截方法。實例以下:
public Object log(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("start log:" + joinPoint.getSignature().getName());//前置 Object object = joinPoint.proceed();//啓用被攔截的核心業務方法 System.out.println("end log:" + joinPoint.getSignature().getName());//後置 return object; }
單個Aspect狀況:
多個Aspect狀況:
爲了便於理解,還能夠參考以下的圖:
將切面想象成同心圓,@Order(n)中n越小圓越大,越先執行。
而先執行的後退出。
示例程序:
aspect:
package com.myspring.aspect; 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//這裏務必記得要把aspect也加入到容器裏 public class Log{ //實際中儘可能不要複用切入點表達式,而是聲明一個切入點方法 @After("execution(* com.myspring.imple..*(..))") public void after() { System.out.println("after"); } @Around("execution(* com.myspring.imple..*(..))") public Object log(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("start log:" + joinPoint.getSignature().getName()); Object object = joinPoint.proceed(); System.out.println("end log:" + joinPoint.getSignature().getName()); return object; } @Before( "execution(* com.myspring.imple..*(..))") public void before() { System.out.println("before"); } }
service:
package com.myspring.imple; import org.springframework.stereotype.Component; @Component("s") public class ServiceImpl { public void save() { System.out.println("save run"); } }
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" <!--添加aop命名空間--> xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <context:component-scan base-package="com.myspring.imple"/> <context:component-scan base-package="com.myspring.aspect"/> <!-- spring調用了aspectj——一個面向切面的框架的自動代理 --> <aop:aspectj-autoproxy/> </beans>
test:
package com.myspring.test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.myspring.imple.ServiceImpl; public class test { public static void main(String[] args) { // TODO 自動生成的方法存根 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //若是核心業務實現了接口則爲JDK動態代理,須要強轉爲接口,不然爲CGLib動態代理,強轉爲實現類 ServiceImpl imp1 = (ServiceImpl)context.getBean("s"); imp1.save(); } }
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:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <!--首先將核心功能和周邊功能的Bean加載到容器裏--> <bean id="logAspect" class="com.how2java.aspect.LoggerAspect"></bean> <bean id="service" class="com.how2java.service.ProductService"></bean> <aop:config> <!-- 聲明一個切入點 --> <aop:pointcut expression="execution(* com.how2java.service.ProductService.*(..))" id="logPointcut"/> <!-- 聲明切面 --> <aop:aspect ref="logAspect" id="log" > <!-- 設定aspect中advice的方法與綁定的切入點 --> <!--使用已經定義好的pointcut--> <aop:around method="around" pointcut-ref="logPointcut"/> <aop:before method="before" pointcut-ref="logPointcut"/> <!--使用切入點表達式定義Pointcut--> <aop:after method="after" pointcut="execution(* com.how2java.service.ProductService.*(..))"/> </aop:aspect> </aop:config> </beans>
名字太多記不住?咱們來理一遍。
首先加載兩個bean:
做爲切面的bean——logAspect以及做爲核心功能的bean——service。對應註解模式的@Component
而後開始配置aop:
攔截住核心功能的一個方法(聲明一個pointcut),名字叫logPointcut,對應註解@Pointcut
接着將做爲切面的bean——logAspect聲明爲一個切面,名字是log,對應註解@Aspect
如今切入點和切面都有了,最後將他們編織在一塊兒:
在切面中設置advice類型和周邊功能方法,而且與相應的切入點——logPointcut綁定,對應註解@Before(切入點表達式或已定義的切入點)等
ps:Pointcut定義在Aspect外則全部Aspect均可以捕獲,定義在某個Aspect裏則只有該Aspect內的Advice能夠捕獲
當咱們使用別人寫好的周邊功能與咱們寫的核心業務編織時,應當使用配置文件方式;當咱們獨立完成而且封裝整個系統時才考慮使用註解方式。所以推薦使用配置文件方式。